我使用下面的方法同时异步地做一些任务:
public async Task<Dictionary<string, object>> Read(string[] queries)
{
var results = queries.Select(query => new Tuple<string, Task<object>>(query, LoadDataAsync(query)));
await Task.WhenAll(results.Select(x => x.Item2).ToArray());
return results
.ToDictionary(x => x.Item1, x => x.Item2.Result);
}
我希望该方法同时为数组中的每个字符串调用 LoadDataAsync
,然后等待所有任务完成并返回结果。
- 如果我像这样运行方法,它会为每个项目调用两次
LoadDataAsync
,一次在await ...
行,一次在最后的.Result
属性 getter 。 - 如果我删除
await ...
行,Visual Studio 会警告我整个方法将并行运行,因为方法内部没有await
调用.
我做错了什么?
有没有更好(更短)的方法来做同样的事情?
最佳答案
再一次:
如果我可以教人们关于 LINQ 的一件事,那就是查询的值是执行查询的对象,而不是执行查询的结果。
您创建一次查询,生成一个可以执行查询的对象。然后执行查询两次。不幸的是,您创建了一个查询,它不仅计算一个值而且会产生副作用,因此,执行两次查询会产生两次副作用。 永远不要制作会产生副作用的可重用查询对象。查询是一种提问 的机制,因此得名。它们并非旨在成为一种控制流机制,但这正是您使用它们的目的。
执行查询两次会产生两个不同的结果,因为查询的结果在两次执行之间当然会发生变化。例如,如果查询正在查询数据库,则数据库可能在两次执行之间发生了变化。如果您的查询是“伦敦每位顾客的姓氏是什么?”答案可能从毫秒改变到毫秒,但问题保持不变。永远记住,查询代表一个问题。
我会倾向于写一些没有疑问的东西。使用“foreach”循环产生副作用。
public async Task<Dictionary<string, object>> Read(IEnumerable<string> queries)
{
var tasks = new Dictionary<string, Task<object>>();
foreach (string query in queries)
tasks.Add(query, LoadDataAsync(query));
await Task.WhenAll(tasks.Values);
return tasks.ToDictionary(x => x.Key, x => x.Value.Result);
}
关于c# - 异步任务被评估两次,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26124676/