Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/wpf/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# WPF应用程序中实现多个异步任务的调度程序_C#_Wpf_Async Await_Task Parallel Library_Dispatcher - Fatal编程技术网

C# WPF应用程序中实现多个异步任务的调度程序

C# WPF应用程序中实现多个异步任务的调度程序,c#,wpf,async-await,task-parallel-library,dispatcher,C#,Wpf,Async Await,Task Parallel Library,Dispatcher,在下面的WPF应用程序的MSDN示例中,演示了多个异步任务的async/await实现,显然没有使用/需要调度程序对象,即异步执行的任务似乎可以直接访问UI控件(在本例中,resultTextBoxTextBox控件-请参阅resultTextBox.Text+=String.Format(“\r\n下载长度:{0}”,长度);)。该应用程序已经过测试,性能与预期一致 但是,如果此实现能够正确处理可能的竞争条件,例如,如果等待和完成的任务试图访问该文本框控件,而后者仍在处理先前完成的任务的更新,

在下面的
WPF
应用程序的MSDN示例中,演示了多个异步
任务的
async/await
实现
,显然没有使用/需要
调度程序
对象,即异步执行的
任务
似乎可以直接访问UI控件(在本例中,
resultTextBox
TextBox
控件-请参阅
resultTextBox.Text+=String.Format(“\r\n下载长度:{0}”,长度);
)。该应用程序已经过测试,性能与预期一致

但是,如果此实现能够正确处理可能的竞争条件,例如,如果等待和完成的
任务
试图访问该
文本框
控件,而后者仍在处理先前完成的
任务的更新,则问题仍然存在ode>?在实际意义上,是否仍然需要WPF
Dispatcher
对象来处理
异步/等待
多任务实现
中的潜在并发/竞争条件问题(或者,可能是,在这种异步/等待编程构造中以某种方式隐式实现了联锁功能)

清单1.MSDN文章“启动多个异步任务,并在任务完成时对其进行处理”()

使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Threading.Tasks;
使用System.Windows;
使用System.Windows.Controls;
使用System.Windows.Data;
使用System.Windows.Documents;
使用System.Windows.Input;
使用System.Windows.Media;
使用System.Windows.Media.Imaging;
使用System.Windows.Navigation;
使用System.Windows.Shapes;
//为System.Net.Http添加using指令和引用。
使用System.Net.Http;
//添加以下using指令。
使用系统线程;
命名空间进程任务完成
{
公共部分类主窗口:窗口
{
//声明System.Threading.CancellationTokenSource。
取消源cts;
公共主窗口()
{
初始化组件();
}
私有异步无效开始按钮单击(对象发送方,路由目标)
{
resultsTextBox.Clear();
//实例化CancellationTokenSource。
cts=新的CancellationTokenSource();
尝试
{
等待访问WebAsync(cts.Token);
resultsTextBox.Text+=“\r\n下载完成。”;
}
捕获(操作取消异常)
{
resultsTextBox.Text+=“\r\n已取消下载。\r\n”;
}
捕获(例外)
{
resultsTextBox.Text+=“\r\n下载失败。\r\n”;
}
cts=null;
}
私有作废取消按钮\u单击(对象发送方,路由目标)
{
如果(cts!=null)
{
cts.Cancel();
}
}
异步任务访问WebAsync(取消令牌ct)
{
HttpClient=新的HttpClient();
//列出网址。
List urlList=设置urlList();
//***创建一个查询,该查询在执行时返回一组任务。
IEnumerable下载任务库=
从url列表中的url选择ProcessURL(url、客户端、ct);
//***使用ToList执行查询并启动任务。
List downloadstasks=downloadstasksquery.ToList();
//***添加一个循环,一次处理一个任务,直到没有剩余任务。
while(downloadTasks.Count>0)
{
//确定完成的第一个任务。
Task firstFinishedTask=等待任务。WhenAny(下载任务);
//***从列表中删除所选任务,这样您就不会
//处理它不止一次。
下载任务。删除(firstFinishedTask);
//等待完成任务。
int length=等待firstFinishedTask;
resultsTextBox.Text+=String.Format(“\r\n下载长度:{0}”,长度);
}
}
私有列表SetUpURLList()
{
列表URL=新列表
{ 
"http://msdn.microsoft.com",
"http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
"http://msdn.microsoft.com/en-us/library/hh290136.aspx",
"http://msdn.microsoft.com/en-us/library/dd470362.aspx",
"http://msdn.microsoft.com/en-us/library/aa578028.aspx",
"http://msdn.microsoft.com/en-us/library/ms404677.aspx",
"http://msdn.microsoft.com/en-us/library/ff730837.aspx"
};
返回URL;
}
异步任务进程url(字符串url、HttpClient客户端、CancellationToken ct)
{
//GetAsync返回一个任务。
HttpResponseMessage response=wait client.GetAsync(url,ct);
//从HttpResponseMessage检索网站内容。
byte[]urlContents=wait response.Content.ReadAsByteArrayAsync();
返回urlContents.Length;
}
}
}
注意:我要感谢Stephen Cleary,感谢他出色的回答和富有洞察力的解释,还想强调他在解决方案中提出的建议改进,即:用相当紧凑的解决方案封装在一行代码中,即:
wait Task.whalll(downloadTasks);
(顺便说一句,我在许多实际应用程序中使用了这个替代方案,特别是在线市场数据应用程序,交易w/多支股票w
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add a using directive and a reference for System.Net.Http.
using System.Net.Http;

// Add the following using directive.
using System.Threading;


namespace ProcessTasksAsTheyFinish
{
    public partial class MainWindow : Window
    {
        // Declare a System.Threading.CancellationTokenSource.
        CancellationTokenSource cts;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Instantiate the CancellationTokenSource.
            cts = new CancellationTokenSource();

            try
            {
                await AccessTheWebAsync(cts.Token);
                resultsTextBox.Text += "\r\nDownloads complete.";
            }
            catch (OperationCanceledException)
            {
                resultsTextBox.Text += "\r\nDownloads canceled.\r\n";
            }
            catch (Exception)
            {
                resultsTextBox.Text += "\r\nDownloads failed.\r\n";
            }

            cts = null;
        }


        private void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            if (cts != null)
            {
                cts.Cancel();
            }
        }


        async Task AccessTheWebAsync(CancellationToken ct)
        {
            HttpClient client = new HttpClient();

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            // ***Create a query that, when executed, returns a collection of tasks.
            IEnumerable<Task<int>> downloadTasksQuery =
                from url in urlList select ProcessURL(url, client, ct);

            // ***Use ToList to execute the query and start the tasks. 
            List<Task<int>> downloadTasks = downloadTasksQuery.ToList();

            // ***Add a loop to process the tasks one at a time until none remain.
            while (downloadTasks.Count > 0)
            {
                    // Identify the first task that completes.
                    Task<int> firstFinishedTask = await Task.WhenAny(downloadTasks);

                    // ***Remove the selected task from the list so that you don't
                    // process it more than once.
                    downloadTasks.Remove(firstFinishedTask);

                    // Await the completed task.
                    int length = await firstFinishedTask;
                    resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
            }
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "http://msdn.microsoft.com",
                "http://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "http://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "http://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "http://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "http://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "http://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        async Task<int> ProcessURL(string url, HttpClient client, CancellationToken ct)
        {
            // GetAsync returns a Task<HttpResponseMessage>. 
            HttpResponseMessage response = await client.GetAsync(url, ct);

            // Retrieve the website contents from the HttpResponseMessage.
            byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

            return urlContents.Length;
        }
    }
}
async Task AccessTheWebAsync(CancellationToken ct)
{
  HttpClient client = new HttpClient();

  // Make a list of web addresses.
  List<string> urlList = SetUpURLList();

  // ***Create a query that, when executed, returns a collection of tasks.
  IEnumerable<Task> downloadTasksQuery =
        from url in urlList select DownloadAndUpdateAsync(url, client, ct);

  // ***Use ToList to execute the query and start the tasks. 
  List<Task> downloadTasks = downloadTasksQuery.ToList();

  await Task.WhenAll(downloadTasks);
}

async Task DownloadAndUpdateAsync(string url, HttpClient client, CancellationToken ct)
{
  var length = await ProcessURLAsync(url, client, ct);
  resultsTextBox.Text += String.Format("\r\nLength of the download:  {0}", length);
}

async Task<int> ProcessURLAsync(string url, HttpClient client, CancellationToken ct)
{
  // GetAsync returns a Task<HttpResponseMessage>. 
  HttpResponseMessage response = await client.GetAsync(url, ct);

  // Retrieve the website contents from the HttpResponseMessage.
  byte[] urlContents = await response.Content.ReadAsByteArrayAsync();

  return urlContents.Length;
}