Joh*_* Wu 4 .net c# asynchronous task
我有一个程序,必须处理多个对象并进行分析。每个分析都会产生一个字符串,并将这些字符串连接起来以创建一个报告。报告需要按一定顺序排列结果,但我想异步分析每个项目,因此我将所有内容放入字典中进行管理,然后可以在准备最终输出之前对其进行排序。
注意:在本例中,我将假装我们正在分析当前程序集中的类型,尽管在我看来,它比这要复杂得多。
(我认为)执行此操作的基本模式如下:
var types = myAssembly.GetTypes();
var tasks = types.ToDictionary( key => key, value => AnalyzeType(value) );
//AnalyzeType() is an async method that returns Task<string>.
Run Code Online (Sandbox Code Playgroud)
现在我们有了一个热门任务字典,由于我什么都没等,所以在创建字典时可能会完成也可能不会完成。
现在获取结果。我该怎么做?
等待
从理论上讲,我要做的就是等待每个任务。等待操作的结果是值本身。但这并不能转换任何东西。
var results = tasks.ToDictionary( k => k.key, async v => await v.Value );
Console.WriteLine(results.GetType().FullName);
Run Code Online (Sandbox Code Playgroud)
输出:
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]]
我感到困惑……我想通过将awaitTask 放在前面,C#会将其转换为结果。但是我还有任务字典。
GetResult()
另一种方法是使用此方法:
var results = tasks.ToDictionary( key => key, value => value.GetAwaiter().GetResult() );
Console.WriteLine(results.GetType().FullName);
Run Code Online (Sandbox Code Playgroud)
输出:
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));
Run Code Online (Sandbox Code Playgroud)
...直到所有任务都完成运行为止(因为可能要花一些时间),以便进行控制,所以我的总体解决方案是:
await Task.WhenAll(tasks.Select( kvp => kvp.Value));
var results = tasks.ToDictionary( key => key, value => value.GetAwaiter().GetResult() );
Console.WriteLine(results.GetType().FullName);
Run Code Online (Sandbox Code Playgroud)
我猜这可行。但是似乎这不是正确的方法。我对致电表示怀疑,如果不需要,GetAwaiter().GetResult()也不想做多余的WhenAll()步骤,也不应该这样做,因为我会分别获得每个任务的等待者和结果。
什么是正确的方法?为什么await我的第一个示例中的关键字不起作用?我需要GetResult()吗?如果可以的话,包含还是一个好主意await Task.WhenAll(),或者仅仅依靠该GetAwaiter()调用(无论如何稍后会发生)会更好吗?
感谢Shaun的正确答案。如果有人想要将其放入代码库中,这是一种可推广的扩展方法。
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);
}
Run Code Online (Sandbox Code Playgroud)
为什么第一个示例中的await关键字不起作用?
的await关键字解包Task<T>的的范围内async的方法,上式的基本结果进行操作<T>,并封装async在一个方法的返回值返回Task。这就是为什么每一个async方法/函数返回的一个void,Task或者Task<T>(注意,void是最合适不过的事件)。的async方法不返回展开的值; 我们从未见过像这样的方法签名public async int SomeMethod(),因为返回int不会在async方法中编译。它需要返回一个Task<int>代替。
什么是正确的方法?
这是一种使用Fiddle的方法将具有类型值的字典转换为具有类型值的Task<T>字典<T>:
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;
public class Program
{
public async static void Main()
{
// create a dictionary of 10 tasks
var tasks = Enumerable.Range(0, 10)
.ToDictionary(i => i, i => Task.FromResult(i * i));
// await all their results
// mapping to a collection of KeyValuePairs
var pairs = await Task.WhenAll(
tasks.Select(
async pair =>
new KeyValuePair<int, int>(pair.Key, await pair.Value)));
var dictionary = pairs.ToDictionary(p => p.Key);
System.Console.WriteLine(dictionary[2].Value); // 4
}
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1524 次 |
| 最近记录: |