繁体   English   中英

使用 async/await 并行处理多个长时间运行的任务

[英]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上的并行性。 这是因为您所做的任何并行工作(即AsParallelParallel.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

WebClient、HttpWebRequest 和 HttpClient 的最大并发请求数

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM