我有一个程序必须处理多个对象并进行分析。每个分析结果都会生成一个字符串,并将这些字符串连接起来以创建报告。该报告需要按一定顺序排列结果,但我想异步分析每个项目,因此我通过将所有内容放入字典中来管理它,然后我可以在准备最终输出之前对其进行排序。
注意:为了这个示例,我将假装我们正在分析当前程序集的类型,尽管在我的例子中它比这更复杂。
这样做的基本模式(我认为)是这样的:
var types = myAssembly.GetTypes();
var tasks = types.ToDictionary( key => key, value => AnalyzeType(value) );
//AnalyzeType() is an async method that returns Task<string>.
现在我们有了一个热门任务的字典,在创建字典时,这些任务可能已完成,也可能尚未完成,因为我没有等待任何东西。
现在就可以得到结果了。我该怎么做?
Await
理论上我所要做的就是等待每项任务; wait 操作的结果是值本身。但这不会转换任何东西。
var results = tasks.ToDictionary( k => k.key, async v => await v.Value );
Console.WriteLine(results.GetType().FullName);
Output:
System.Collections.Generic.Dictionary'2[[System.Type, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.Threading.Tasks.Task'1[[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
我很困惑...我想通过把await
在Task前面,c#会将其转换为结果。但我还有一本任务字典。
获取结果()
另一种方法是使用这个:
var results = tasks.ToDictionary( key => key, value => value.GetAwaiter().GetResult() );
Console.WriteLine(results.GetType().FullName);
Output:
System.Collections.Generic.Dictionary'2[[System.Type, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089],[System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]
所以这似乎得到了我想要的,但我不得不删除await
关键字,现在我从编译器收到警告,该方法将同步执行。
我可以添加
await Task.WhenAll(tasks.Select( kvp => kvp.Value));
...放弃控制权,直到所有任务完成运行(因为可能需要一段时间),所以我的整体解决方案是:
await Task.WhenAll(tasks.Select( kvp => kvp.Value));
var results = tasks.ToDictionary( key => key, value => value.GetAwaiter().GetResult() );
Console.WriteLine(results.GetType().FullName);
我想这有效。但似乎这不是正确的方法;我对打电话表示怀疑GetAwaiter().GetResult()
,我宁愿不做额外的事情WhenAll()
如果不需要,也不应该这样做,因为我正在单独获取每项任务的等待者和结果。
这样做的正确方法是什么?为什么没有await
关键字在我的第一个示例中起作用吗?我需要吗GetResult()
?如果我这样做,包括在内是个好主意吗?await Task.WhenAll()
,还是简单地依赖于GetAwaiter()
调用(无论如何都会稍后发生)?
单击此处获取小提琴如果你想使用它。
编辑(答案):
感谢肖恩的正确答案。如果有人想要一些东西,他们可以将其放入他们的代码库中,这是一个通用的扩展方法。
public static async Task<Dictionary<TKey, TResult>> ToResults<TKey,TResult>(this IEnumerable<KeyValuePair<TKey, Task<TResult>>> input)
{
var pairs = await Task.WhenAll
(
input.Select
(
async pair => new { Key = pair.Key, Value = await pair.Value }
)
);
return pairs.ToDictionary(pair => pair.Key, pair => pair.Value);
}