我有一个帮助方法返回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
时缺少什么?
答案 0 :(得分:2)
parallel 和 concurrent 之间存在差异。并发只是意味着一次做多个事情,而并行意味着在多个线程上做多个事情。 async
非常适合并发,但不会(直接)帮助您实现并行性。
作为一般规则,应避免使用ASP.NET上的并行性。这是因为您所做的任何并行工作(即AsParallel
,Parallel.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
。