簡體   English   中英

使用異步/等待和任務優化火災和遺忘

[英]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 DataflowActionBlockMaxDegreeOfParallelism和單個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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM