c# – 同时等待具有独立延续的多个异步调用

c# – 同时等待具有独立延续的多个异步调用,第1张

概述有几种情况我需要调用多个异步调用(来自相同的事件处理程序),这些异步调用可以彼此独立地进行,每个异步调用都有自己的更新UI继续. 以下天真实现会导致三个异步 *** 作按顺序执行: private async void button_Click(object sender, RoutedEventArgs e){ nameTextBox.Text = await GetNameAsync(); 有几种情况我需要调用多个异步调用(来自相同的事件处理程序),这些异步调用可以彼此独立地进行,每个异步调用都有自己的更新UI继续.

以下天真实现会导致三个异步 *** 作按顺序执行:

private async voID button_Click(object sender,RoutedEventArgs e){    nameTextBox.Text = await GetnameAsync();    cityTextBox.Text = await GetCityAsync();    rankTextBox.Text = await GetRankAsync();}

有一个MSDN example建议将任务的创建与各自的await语句分开,允许它们并行运行:

private async voID button_Click(object sender,RoutedEventArgs e){    var naMetask = GetnameAsync();    var cityTask = GetCityAsync();    var rankTask = GetRankAsync();    nameTextBox.Text = await naMetask;    cityTextBox.Text = await cityTask;    rankTextBox.Text = await rankTask;}

然而,这种方法的局限性在于任务延续仍然是顺序注册的,这意味着第n个延续不能在其所有前面的n-1个连续完成之前执行,即使它的任务可能是第一个完成的.

使异步任务并行运行的最佳模式是什么,但是一旦各自的任务完成,每个延续都会运行?

编辑:大多数答案建议在Task.WhenAll等待,如下所示:

private async voID button_Click(object sender,RoutedEventArgs e){    var naMetask = GetnameAsync();     var cityTask = GetCityAsync();     var rankTask = GetRankAsync();     await Task.WhenAll(naMetask,cityTask,rankTask);    nameTextBox.Text = naMetask.Result;    cityTextBox.Text = cityTask.Result;    rankTextBox.Text = rankTask.Result;}

但是,这不符合我的要求,因为我需要在每个任务完成后立即执行每个继续.例如,假设GetnameAsync需要5秒,GetCityAsync需要2秒,而GetRankAsync需要8秒.上面的实现将导致所有三个文本框仅在8s后更新,即使nameTextBox和cityTextBox的结果早得知.

解决方法 传统方法是使用 ContinueWith为每个异步任务注册相应的延续:

private async voID button_Click(object sender,RoutedEventArgs e){    TaskScheduler uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();    await Task.WhenAll(        GetnameAsync().ContinueWith(naMetask => { nameTextBox.Text = naMetask.Result; },uiScheduler),GetCityAsync().ContinueWith(cityTask => { cityTextBox.Text = cityTask.Result; },GetRankAsync().ContinueWith(rankTask => { rankTextBox.Text = rankTask.Result; },uiScheduler));}

使用C#5,现在最好使用await-style模式.通过将每个任务延续对分成自己的方法,可以最容易地实现这一点:

private async voID button_Click(object sender,RoutedEventArgs e){    await Task.WhenAll(        PopulatenameAsync(),PopulateCityAsync(),PopulaterankAsync());}private async Task PopulatenameAsync(){    nameTextBox.Text = await GetnameAsync();}private async Task PopulateCityAsync(){    cityTextBox.Text = await GetCityAsync();}private async Task PopulaterankAsync(){    rankTextBox.Text = await GetRankAsync();}

定义所有这些简单的方法很快就变得很麻烦,所以可以将它们浓缩成异步lambda:

private async voID button_Click(object sender,RoutedEventArgs e){    await Task.WhenAll(        new Func<Task>(async () => { nameTextBox.Text = await GetnameAsync(); })(),new Func<Task>(async () => { cityTextBox.Text = await GetCityAsync(); })(),new Func<Task>(async () => { rankTextBox.Text = await GetRankAsync(); })());}

如果经常使用这种模式,那么定义一个可以采用Func< Task>的实用程序方法也会很有帮助. lambdas并执行它们,使我们的事件处理程序代码更简洁和可读:

public static Task WhenAllTasks(params Func<Task>[] taskProducers){    return Task.WhenAll(taskProducers.Select(taskProducer => taskProducer()));}private async voID button_Click(object sender,RoutedEventArgs e){    await WhenAllTasks(        async () => nameTextBox.Text = await GetnameAsync(),async () => cityTextBox.Text = await GetCityAsync(),async () => rankTextBox.Text = await GetRankAsync());}
总结

以上是内存溢出为你收集整理的c# – 同时等待具有独立延续的多个异步调用全部内容,希望文章能够帮你解决c# – 同时等待具有独立延续的多个异步调用所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/1219008.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-05
下一篇 2022-06-05

发表评论

登录后才能评论

评论列表(0条)

保存