[英]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.