我使用以下方法异步并同时执行某些任务:
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会警告我整个方法将并行运行,因为方法内部没有等待调用.
我究竟做错了什么?
是否有更好(更短)的方法来做同样的事情?
解决方法
再来一次:
如果我可以教人们关于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); }