使用async/await并行执行多个长时间运行的任务
我有一个辅助方法返回IEnumerable<string>
。随着收藏的增长,它正在大幅减速。我目前的做法是基本上做到以下几点:使用async/await并行执行多个长时间运行的任务
var results = new List<string>();
foreach (var item in items)
{
results.Add(await item.Fetch());
}
我实际上并不确定这是否异步给我任何利益(它肯定不会似乎喜欢它),但是所有的方法堆栈和我的控制器的动作are asynchronous:
public async Task<IHttpActionResult> FetchAllItems()
因为这些代码最终使用我的API,我真的很想并行这些所有的东西,我希望将是巨大的速度提升。我试过.AsParallel:
var results = items
.AsParallel()
.Select(i => i.Fetch().Result)
.AsList();
return results;
而且.WhenAll(返回一个字符串[]):
var tasks = items.Select(i => i.Fetch());
return Task<string>.WhenAll<string>(tasks).Result;
和解雇了所有长时间运行的作业,并依次等待他们的最后努力(希望他们都并行运行,所以在等待一个会让所有其他接近完成):
var tasks = new LinkedList<Task<string>>();
foreach (var item in items)
tasks.AddLast(item.Fetch());
var results = new LinkedList<string>();
foreach (var task in tasks)
results.AddLast(task.Result);
在每次测试情况下,需要运行的时间是成正比的ITE的数量女士。这样做没有可辨别的加速。我在使用任务时丢失了什么,并且await
/async
?
有平行和并发之间的差异。并发意味着一次只能做多件事,而并行意味着在多个线程上做不止一件事。 async
非常适合并发性,但不会(直接)帮助您进行并行处理。
作为一般规则,应避免ASP.NET上的并行性。这是因为您所做的任何并行工作(即AsParallel
,Parallel.ForEach
等)与ASP.NET共享相同的线程池,因此降低了ASP.NET处理其他请求的能力。这会影响Web服务的可伸缩性。最好将线程池留给ASP.NET。
但是,并发性很好 - 特别是异步并发。这是Task.WhenAll
进来这样的代码是你应该寻找什么(注意,是Task<T>.Result
没有呼叫):
var tasks = items.Select(i => i.Fetch());
return await Task<string>.WhenAll<string>(tasks);
鉴于你的其他代码样本,这将有利于通过您的通话时同时运行从Fetch
开始,并用await
替换所有Result
调用。这可能是(部分)你的问题,因为Result
强制同步执行。
另一个可能的问题是正在提取的底层资源不支持并发访问,或者可能存在您不知道的限制。例如,如果Fetch
从其他Web服务检索数据,请检查System.Net.ServicePointManager.DefaultConnectionLimit
。
也许我只是不想'async' /'await',我想在调试器中看到结果本身而不是任务。如果我的整个调用链(一直到控制器的动作)都是'async',我可以简单地实现'WhenAll'(如你的代码示例中)并且改进我当前的'foreach'实现的性能吗? – user655321 2014-10-31 15:34:12
是的,只要底层资源允许并发访问并且不存在限制。 – 2014-10-31 15:39:21
异步不会为您提供改进算法性能的好处,相反,它将允许您的主线程(UI)在工作完成时保持响应。 – 2014-10-31 02:49:04
如果任务CPU绑定,应该看到加速比例与CPU数量成正比,除非你的代码有一些东西(比如共享'lock'下的大块代码),它完全阻止它并行运行或者CPU已经处于100%其他处理。 – 2014-10-31 02:54:24
@JohnKoerner:'.AsParallel'会为这些异步任务提供加速吗?我最初开始将我的'foreach'调整为'Parallel.ForEach',然后我走进了一个异步/等待世界。 – user655321 2014-10-31 02:58:36