我想尝试使用后台工作者。我很有意思使用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个标签(label1
,label2
,label3
)的值。即所有标签都设置为"待定"当每个任务完成后,他们相应的标签值将变为"完成"。
我希望相应地await
每个任务,以便他们各自执行相应的任务。这意味着如果taskDelay1
的工作完成,请将label1
的文字设置为"已完成" taskDelay2
和taskDelay3
仍有待处理。但如果我在其中放置3 awaits
,程序将等待所有人完成任务,然后继续其余的代码。
如何继续其余的代码,其中每个标签只等待自己的任务完成,然后决定该怎么做?
答案 0 :(得分:5)
您应该使用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);
答案 1 :(得分:4)
parallel 和 concurrent 之间存在重要区别。并行是指使用多个线程来执行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
)。也就是说,它并不完美,因为GUI逻辑与RunDownloadNAsync
方法中的操作逻辑混合在一起。更好的方法是使用IProgress<T>
和Progress<T>
来更新UI。
答案 2 :(得分:1)
Task
完成后,您可以使用ContinueWith
执行操作。请注意,您可能需要在ContinueWith
中的表达式中调用Dispatcher.Invoke
以防止跨线程调用。
以下是来自WPF应用程序的示例,使用Dispatcher.Invoke
:
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
的替代方法是使用任务调度程序,例如TaskScheduler.FromCurrentSynchronizationContext()
如果你在UI线程上。