c# – 同时等待具有独立延续的多个异步调用
有几种情况我需要调用多个异步调用(来自相同的事件处理程序),这些异步调用可以彼此独立地进行,每个异步调用都有自己的更新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个连续完成之前执行,即使它的任务可能是第一个完成的. 使异步任务并行运行的最佳模式是什么,但是一旦各自的任务完成,每个延续都会运行? 编辑:大多数答案建议在 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()); } (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |