![](/img/trans.png)
[英]What is correct way to combine long-running tasks with async / await pattern?
[英]Parallelizing multiple long-running tasks with async/await
我有一个辅助方法返回IEnumerable<string>
。 随着收藏的增长,它正在急剧放缓。 我当前的方法基本上是执行以下操作:
var results = new List<string>();
foreach (var item in items)
{
results.Add(await item.Fetch());
}
我实际上不确定这种异步性是否给我带来了任何好处(看起来肯定不像),但是堆栈上的所有方法和我的控制器的操作都是异步的:
public async Task<IHttpActionResult> FetchAllItems()
由于此代码最终由我的 API 使用,因此我真的很想将所有这些并行化,以实现我希望的巨大加速。 我试过了。AsParallel:
var results = items
.AsParallel()
.Select(i => i.Fetch().Result)
.AsList();
return results;
And.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);
在每个测试用例中,运行时间与项目数量成正比。 这样做没有明显的加速。 我在使用 Tasks 和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
。
与单个服务器的最大连接数也有可配置的限制,这可以使下载性能独立于客户端线程的数量。
要更改连接限制,请使用 ServicePointManager.DefaultConnectionLimit
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.