我使用以下方法异步并同时执行某些任务:
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
属性获取者。await ...
行,Visual Studio会警告我整个方法会并行运行,因为方法内部没有await
次调用。我做错了什么?
有更好(更短)的方法吗?
答案 0 :(得分:26)
再次:
如果我可以向人们传授关于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);
}
答案 1 :(得分:8)
您必须记住LINQ操作返回查询,而不是这些查询的结果。变量results
并不代表您拥有的操作的结果,而是一个能够在迭代时生成这些结果的查询。你迭代它两次,在每个场合执行查询。
您可以在此处执行的操作首先将查询结果具体化为集合,而不是将查询本身存储在results
中。
var results = queries.Select(query => Tuple.Create(query, LoadDataAsync(query)))
.ToList();
await Task.WhenAll(results.Select(x => x.Item2));
return results
.ToDictionary(x => x.Item1, x => x.Item2.Result);
答案 2 :(得分:3)
更好的方法可能是格式化异步调用,以便await使用各自的键返回任务的结果:
public async Task<KeyValuePair<string, object>> LoadNamedResultAsync(string query)
{
object result = null;
// Async query setting result
return new KeyValuePair<string, object>(query, result)
}
public async Task<IDictionary<string, object>> Read(string[] queries)
{
var tasks = queries.Select(LoadNamedResultAsync);
var results = await Task.WhenAll(tasks);
return results.ToDictionary(r => r.Key, r => r.Value);
}
答案 3 :(得分:0)
作为Jesse Sweetland's答案的补充,完全实现的版本:
public async Task<KeyValuePair<string, object>> LoadNamedResultAsync(string query)
{
Task<object> getLoadDataTask = await LoadDataAsync(query);
return new KeyValuePair<string, object>(query, getLoadDataTask.Result);
}
public async Task<IDictionary<string, object>> Read(string[] queries)
{
var tasks = queries.Select(LoadNamedResultAsync);
var results = await Task.WhenAll(tasks);
return results.ToDictionary(r => r.Key, r => r.Value);
}
Rem:我建议将其作为编辑,但由于更改太多而被拒绝。