簡體   English   中英

如何將此Parallel.ForEach代碼轉換為異步/等待

[英]How to convert this Parallel.ForEach code to async/await

我在異步/等待時遇到了一些麻煩。 我正在幫助具有以下代碼(為簡便起見,已簡化)的現有代碼庫:

List<BuyerContext> buyerContexts = GetBuyers();
var results = new List<Result>();

Parallel.ForEach(buyerContexts, buyerContext =>
{
    //The following call creates a connection to a remote web server that 
    //can take up to 15 seconds to respond
    var result = Bid(buyerContext);

    if (result != null)
        results.Add(result);
}

foreach (var result in results)
{
  // do some work here that is predicated on the 
  // Parallel.ForEach having completed all of its calls
}

如何使用異步/等待將此代碼轉換為異步代碼而不是並行代碼? 我遇到了一些非常嚴重的性能問題,我認為這是對多個網絡I / O操作使用並行方法的結果。

我已經嘗試了幾種方法,但是從Visual Studio中收到警告,我的代碼將同步執行,或者我不能在async方法之外使用await關鍵字,因此我確定我只是缺少一些簡單的方法。

編輯#1:我也開放異步/等待替代品。 根據我到目前為止的閱讀,這似乎是正確的方法。

編輯#2:此應用程序是Windows服務。 它呼喚幾個“買方”,要求他們對特定數據進行競標。 在繼續處理之前,我需要所有出價。

“使事物異步”的關鍵是從葉子開始。 在這種情況下,請從您的網絡代碼(未顯示)開始,並將您擁有的任何同步調用(例如WebClient.DownloadString )更改為相應的異步調用(例如HttpClient.GetStringAsync )。 然后await該呼叫。

使用await將強制調用方法async ,並將其返回類型從T更改為Task<T> 此時,添加Async后綴也是一個好主意,以便您遵循眾所周知的約定 然后,使用方法的所有調用者,並將它們也更改為使用await ,這將要求它們成為async等。重復上述步驟,直到可以使用BidAsync方法為止。

然后,您應該考慮更換並行循環。 使用Task.WhenAll很容易做到這Task.WhenAll

List<BuyerContext> buyerContexts = GetBuyers();
var tasks = buyerContexts.Select(buyerContext => BidAsync(buyerContext));
var results = await Task.WhenAll(tasks);

foreach (var result in results)
{
  ...
}

基本上,要使用async-awaitBid方法應具有此簽名而不是當前簽名:

public async Task<Result> BidAsync(BuyerContext buyerContext);

這將允許您在此方法中使用await 現在,每次撥打網絡電話時,基本上都需要await 例如,以下是將同步方法的調用和簽名修改為異步方法的方法。

之前

//Signature
public string ReceiveStringFromClient();

//Call
string messageFromClient = ReceiveStringFromClient();

//Signature
public Task<string> ReceiveStringFromClientAsync();

//Call
string messageFromClient = await ReceiveStringFromClientAsync();

如果仍然需要對這些方法進行同步調用,我建議創建后綴為“ Async”的新方法。

現在,您需要在每個級別上執行此操作,直到達到網絡調用為止,此時您將可以等待.Net的async方法。 它們通常與同步版本具有相同的名稱,后綴為“異步”。

完成所有這些操作后,您可以在主代碼中使用它。 我會按照以下方式做一些事情:

List<BuyerContext> buyerContexts = GetBuyers();
var results = new List<Result>();

List<Task> tasks = new List<Task>();

//There really is no need for Parallel.ForEach unless you have hundreds of thousands of requests to make.
//If that's the case, I hope you have a good network interface!
foreach (var buyerContext in buyerContexts)
{
    var task = Task.Run(async () =>
    {
        var result = await BidAsync(buyerContext);        

        if (result != null)
            results.Add(result);
    });

    tasks.Add(task);
}

//Block the current thread until all the calls are completed
Task.WaitAll(tasks);

foreach (var result in results)
{
  // do some work here that is predicated on the 
  // Parallel.ForEach having completed all of its calls
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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