[英]Optimizing for fire & forget using async/await and tasks
我有大约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<HttpResponseMessage> Update(string url)
{
var client = new HttpClient();
var response = await client.SendAsync(url).ConfigureAwait(false);
//log response.
}
更新:我实际上正在获取TaskCanceledExceptions。 我的系统是否耗尽了线程? 我该怎么做才能避免这种情况?
您的方法将同时启动所有任务,这可能不是您想要的。 不会涉及任何线程,因为async
操作没有线程 ,但是并发连接数可能会有所限制。
可能有更好的工具来做到这一点,但如果你想使用异步/等待一个选择是使用斯蒂芬Toub的ForEachAsync
在记录这篇文章 。 它允许您控制要执行的同时操作数量,因此不会超出连接限制。
这里是文章的内容:
public static class Extensions
{
public static async Task ExecuteInPartition<T>(IEnumerator<T> partition, Func<T, Task> body)
{
using (partition)
while (partition.MoveNext())
await body(partition.Current);
}
public static Task ForEachAsync<T>(this IEnumerable<T> source, int dop, Func<T, Task> 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<Item> items)
{
var block = new ActionBlock<Item>(
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
(您应针对具体情况进行测试和优化)来限制该数字。 重要的是要注意,TPL认为合适时可以选择一个较小的数字。 async
方法或lambda表达式的末尾进行单个async
调用时,最好删除冗余的async-await
并仅返回任务(例如return block.Completion;
),以return block.Completion;
Complete
将通知ActionBlock
不接受任何其他项目,但完成处理它已经拥有的项目。 完成后, Completion
任务将完成,因此您可以await
它。 我怀疑您正在遭受传出连接管理的困扰,这阻止了到同一域的大量同时连接。 这个广泛的问题解答中给出的答案可能会为您提供一些调查的途径。
什么限制了我的ASP.NET应用程序可以与Web服务建立的同时连接数?
就您的代码结构而言,我个人将尝试使用动态连接池。 您知道实际上无法同时获得500万个连接,因此尝试尝试将无法正常工作-您最好处理(例如)20个连接的合理且已配置的限制,并在一个池中使用它们。 这样您可以调高或调低。
或者,您可以研究专门用于您正在执行的工作(编排Http请求)的HTTP Pipelining(我尚未使用)。 http://en.wikipedia.org/wiki/HTTP_pipelining
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.