3个并行任务,每个任务等待自己的结果

时间:2014-12-08 12:41:52

标签: c# asynchronous async-await

我想尝试使用后台工作者。我很有意思使用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个标签(label1label2label3)的值。即所有标签都设置为"待定"当每个任务完成后,他们相应的标签值将变为"完成"。

我希望相应地await每个任务,以便他们各自执行相应的任务。这意味着如果taskDelay1的工作完成,请将label1的文字设置为"已完成" taskDelay2taskDelay3仍有待处理。但如果我在其中放置3 awaits,程序将等待所有人完成任务,然后继续其余的代码。

如何继续其余的代码,其中每个标签只等待自己的任务完成,然后决定该怎么做?

3 个答案:

答案 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());
}

此方法可避免创建不必要的线程,也不会使用过时的技术(ContinueWithDispatcher.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线程上。