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

时间:2014-10-31 02:46:37

标签: c# asynchronous async-await

我有一个帮助方法返回IEnumerable<string>。随着收集的增长,它正在急剧减速。我目前的方法是基本上做以下几点:

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);

在每个测试用例中,运行所需的时间与项目数成正比。这样做没有明显的加速。我在使用任务和await / async时缺少什么?

1 个答案:

答案 0 :(得分:2)

parallel concurrent 之间存在差异。并发只是意味着一次做多个事情,而并行意味着在多个线程上做多个事情async非常适合并发,但不会(直接)帮助您实现并行性。

作为一般规则,应避免使用ASP.NET上的并行性。这是因为您所做的任何并行工作(即AsParallelParallel.ForEach等)与ASP.NET共享相同的线程池,因此降低了ASP.NET处理其他的能力em>请求。这会影响Web服务的可伸缩性。最好将线程池保留给ASP.NET。

然而,并发性很好 - 特别是异步并发。这就是Task.WhenAll的用武之地。像这样的代码是您应该寻找的(请注意,没有调用Task<T>.Result):

var tasks = items.Select(i => i.Fetch());
return await Task<string>.WhenAll<string>(tasks);

根据您的其他代码示例,最好从Fetch开始运行您的调用树,并用Result替换所有await次调用。这可能是(部分)您的问题,因为Result强制同步执行。

另一个可能的问题是被提取的底层资源不支持并发访问,或者可能存在您不知道的限制。例如,如果Fetch从其他网络服务检索数据,请查看System.Net.ServicePointManager.DefaultConnectionLimit