[英]3 parallel tasks each awaiting their own result
我想尝试使用后台工作者。 我使用async/await
很有趣。 我有3个并行任务。
private async void RunDownloadsAsync()
{
Task taskDelay1 = Task.Run(() => Task.Delay(10000));
Task taskDelay2 = Task.Run(() => Task.Delay(15000));
Task taskDelay3 = Task.Run(() => Task.Delay(20000));
await ???
}
让我们假设这些任务中的每一个在完成后都会更改表单上的3个Labels( label1
, label2
, label3
)的值。 也就是说,所有标签都设置为“待处理”,并且当每个任务完成时,它们相应的标签值将更改为“完成”。
我想相应地await
每个任务,以便它们各自执行各自的任务。 这意味着,如果taskDelay1
的作业完成,则在taskDelay2
和taskDelay3
仍处于挂起状态时,将label1
的文本设置为“ Finished”。 但是,如果我awaits
那儿awaits
3个,程序将等待所有这些人完成任务,然后继续其余代码。
我如何继续其余的代码,其中每个标签仅等待其自己的任务完成,然后决定要做什么?
您应该使用Task.WhenAll
方法等待所有这些方法,并在任务完成时使用ContinueWith
执行操作。
Task taskDelay1 = Task.Run(() => Task.Delay(10000)).ContinueWith(t => SetLabel());
Task taskDelay2 = Task.Run(() => Task.Delay(15000)).ContinueWith(t => SetLabel());
Task taskDelay3 = Task.Run(() => Task.Delay(20000)).ContinueWith(t => SetLabel());
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
并行和并发之间有一个重要的区别。 并行是指使用多个线程来执行CPU绑定的工作。 并行是并发的一种形式,但不是唯一的一种。 并发的另一种形式是异步,它可以执行不受CPU约束的工作,而无需引入多个线程。
在您的情况下,“下载”意味着一个受I / O约束的操作,该操作应异步进行,而不是并行操作。 检出HttpClient
类以进行异步下载。 您可以使用Task.WhenAll
进行异步并发:
private async Task RunDownload1Async()
{
label1.Text = "Pending";
await Task.Delay(10000);
label1.Text = "Finished";
}
private async Task RunDownload2Async()
{
label2.Text = "Pending";
await Task.Delay(15000);
label2.Text = "Finished";
}
private async Task RunDownload3Async()
{
label3.Text = "Pending";
await Task.Delay(20000);
label3.Text = "Finished";
}
private async Task RunDownloadsAsync()
{
await Task.WhenAll(RunDownload1Async(), RunDownload2Async(), RunDownload3Async());
}
这种方法避免创建不必要的线程,并且不使用过时的技术( ContinueWith
, Dispatcher.Invoke
)。 就是说,这并不完美,因为在RunDownloadNAsync
方法中,GUI逻辑与操作逻辑混合在一起。 更好的方法是使用IProgress<T>
和Progress<T>
来更新UI。
您可以在Task
完成后使用ContinueWith
执行操作。 请注意,您可能需要在ContinueWith
中的表达式中调用Dispatcher.Invoke
来防止跨线程调用。
这是使用Dispatcher.Invoke
的WPF应用程序示例:
private static async void Async()
{
Task taskDelay1 = Task.Run(() => Task.Delay(1000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label1.Content = "One done"));
Task taskDelay2 = Task.Run(() => Task.Delay(1500))
.ContinueWith(x => Dispatcher.Invoke(() => this.label2.Content = "Two done"));
Task taskDelay3 = Task.Run(() => Task.Delay(2000))
.ContinueWith(x => Dispatcher.Invoke(() => this.label3.Content = "Three done"));
await Task.WhenAll(taskDelay1, taskDelay2, taskDelay3);
}
正如Dirk所建议的那样,调用Dispatcher.Invoke
的替代方法是使用任务调度程序,例如,如果您在UI线程上,则使用TaskScheduler.FromCurrentSynchronizationContext()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.