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>?在实际意义上,是否仍然需要WPFDispatcher
对象来处理异步/等待
多任务实现中的潜在并发/竞争条件问题(或者,可能是,在这种异步/等待编程构造中以某种方式隐式实现了联锁功能)
清单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;
}