使用async / await和任务优化火灾和忘记
我有大约500万件要更新。 我并不真正关心响应(响应会很好,所以我可以记录它,但我不想要响应,如果这会花费我的时间。)话虽如此,这段代码是否优化为运行为尽可能快? 如果有500万个项目,我是否会冒任务被取消或超时错误的风险? 我每秒钟会得到大约1或2个回复。
var tasks = items.Select(async item => { await Update(CreateUrl(item)); }).ToList(); if (tasks.Any()) { await Task.WhenAll(tasks); } private async Task Update(string url) { var client = new HttpClient(); var response = await client.SendAsync(url).ConfigureAwait(false); //log response. }
更新:我实际上正在获取TaskCanceledExceptions。 我的系统用完线程了吗? 我该怎么做才能避免这种情况?
您的方法将同时启动所有任务,这可能不是您想要的。 不会涉及任何线程,因为使用async
操作没有线程 ,但可能存在多个并发连接限制。
可能有更好的工具来执行此操作,但如果您想使用async / await,则可以使用Stephen Toub的ForEachAsync
如本文所述 。 它允许您控制要执行的同时操作数,因此不会超出连接限制。
这是来自文章:
public static class Extensions { public static async Task ExecuteInPartition(IEnumerator partition, Func body) { using (partition) while (partition.MoveNext()) await body(partition.Current); } public static Task ForEachAsync (this IEnumerable source, int dop, Func body) { return Task.WhenAll( from partition in Partitioner.Create(source).GetPartitions(dop) select ExecuteInPartition(partition, body)); } }
用法:
public async Task UpdateAll() { // Allow for 100 concurrent Updates await items.ForEachAsync(100, async t => await Update(t)); }
一个更好的方法是使用TPL Dataflow
的ActionBlock
和MaxDegreeOfParallelism
以及一个HttpClient
:
Task UpdateAll(IEnumerable- items) { var block = new ActionBlock
- ( item => UpdateAsync(CreateUrl(item)), new ExecutionDataflowBlockOptions {MaxDegreeOfParallelism = 1000}); foreach (var item in items) { block.Post(item); } block.Complete(); return block.Completion; } async Task UpdateAsync(string url) { var response = await _client.SendAsync(url).ConfigureAwait(false); Console.WriteLine(response.StatusCode); }
- 单个
HttpClient
可以同时用于多个请求 ,因此仅创建和处理单个实例而不是500万个更好。 - 在同时触发如此多的请求时存在许多问题:机器的网络堆栈,目标网站,超时等等。
ActionBlock
使用MaxDegreeOfParallelism
(您应根据具体情况测试和优化)对该数字进行MaxDegreeOfParallelism
。 值得注意的是,TPL可能会在认为合适时选择较低的数字。 - 当你在
async
方法或lambda表达式的末尾有一个async
调用时,性能最好删除冗余的async-await
并返回任务(即return block.Completion;
) -
Complete
将通知ActionBlock
不再接受任何项目,但完成它已有的处理项目。 完成后,Completion
任务将完成,以便您可以await
它。
我怀疑您正在遭受传出连接管理,从而阻止大量同时连接到同一个域。 在这个广泛的Q + A中给出的答案可能会为您提供一些调查途径。
限制我的ASP.NET应用程序可以对Web服务进行的同时连接的数量是多少?
就您的代码结构而言,我个人尝试使用动态连接池。 你知道你实际上不能同时获得5米连接,所以试图尝试它将无法工作 – 你也可以处理(例如)20个连接的合理和配置限制并在池中使用它们。 通过这种方式,您可以调高或调低。
或者您可以调查HTTP Pipelining(我没有使用过),它专门用于您正在进行的工作(批处理Http请求)。 http://en.wikipedia.org/wiki/HTTP_pipelining