从异步任务收集结果的更好方法

时间:2017-02-24 16:40:54

标签: c# .net async-await

我的MVC应用程序偶尔导致死锁。我认为这可能是由于我从完成的异步任务中收集数据的错误方式。

我有两个独立的异步方法。

// here I already know that both tasks are completed and 
// I am using (or abusing?) await to get task results
List<string> names1 = await task1.ConfigureAwait(false);
List<string> names2 = await task2.ConfigureAwait(false);

if (names1 != null) total.AddRange(names1);
if (names2 != null) total.AddRange(names2);

问题第1部分:从这些任务中收集结果的最安全的推荐方法和最佳做法是什么:

total.AddRange(task1.IsFaulted ? new List<string> : task1.Result);
total.AddRange(task2.IsFaulted ? new List<string> : task2.Result);

var task1 = GetNamesFromSource1Async().ContinueWith(t => 
 {
   if ( !t.IsFaulted && t.Result != null)
   {
      return t.Result.Take(1).ToList();
   }
 });

问题第2部分:另外如果我想从第一个源转换数据,使用ContinueWith是否安全(当我说安全时我的意思是从死锁的角度来看)

{{1}}

备注:这里我通过检查IsFaulted标志来尝试控制每个任务中的异常。

对于解决此问题的最佳做法的建议将受到高度赞赏。我使用的是.NET 4.5

1 个答案:

答案 0 :(得分:3)

.Result是一个阻塞调用,当与async / await混合时会导致死锁

var task1 = GetNamesFromSource1Async(); // a database call, may throw an exception
var task2 = GetNamesFromSource2Async(); // a database call, may throw an exception

var total = new List<string>();

var results = await Task.WhenAll(task1, task2);

total.AddRange(results.Where(s => s != null && s.Count > 0).SelectMany(s => s));

<强>更新

以上假设返回类型都是相同的。

但是从你的评论......

  

如果我仍然需要收集结果,您将如何修改最后一行?   但是task1和task2是基于不同的类型吗?

并参考此答案

Awaiting multiple Tasks with different results

然后它将被修改为

var task1 = GetNamesFromSource1Async(); // a database call, may throw an exception
var task2 = GetNamesFromSource2Async(); // a database call, may throw an exception

var total = new List<string>();

await Task.WhenAll(task1, task2);

List<String> names1 = await task1;
List<int> names2 = await task2;

//...process results