繁体   English   中英

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

[英]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( label1label2label3 )的值。 也就是说,所有标签都设置为“待处理”,并且当每个任务完成时,它们相应的标签值将更改为“完成”。

我想相应地await每个任务,以便它们各自执行各自的任务。 这意味着,如果taskDelay1的作业完成,则在taskDelay2taskDelay3仍处于挂起状态时,将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());
}

这种方法避免创建不必要的线程,并且不使用过时的技术( ContinueWithDispatcher.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM