如何将此 Parallel.ForEach 代码转换为 async/await

Sco*_*ott 5 c# asynchronous

我在理解 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
}
Run Code Online (Sandbox Code Playgroud)

如何使用 async/await 将此代码转换为异步代码而不是并行代码?我遇到了一些非常严重的性能问题,我认为这是对多个网络 I/O 操作使用并行方法的结果。

我自己尝试了几种方法,但我从 Visual Studio 收到警告,说我的代码将同步执行,或者我不能在异步方法之外使用 await 关键字,所以我确定我只是错过了一些简单的东西。

编辑 #1:我也对 async/await 的替代方案持开放态度。根据我到目前为止的阅读,这似乎是正确的方法。

编辑 #2:此应用程序是 Windows 服务。它召集了几个“买家”,要求他们对特定的数据进行出价。在继续处理之前,我需要收回所有出价。

Phi*_*aré 3

基本上,要使用async-await,该Bid方法应该具有此签名而不是当前的签名:

public async Task<Result> BidAsync(BuyerContext buyerContext);
Run Code Online (Sandbox Code Playgroud)

这将允许您在该方法中使用await。现在,每次进行网络调用时,基本上都需要await它。例如,以下是如何将同步方法的调用和签名修改为异步方法的调用和签名。

//Signature
public string ReceiveStringFromClient();

//Call
string messageFromClient = ReceiveStringFromClient();
Run Code Online (Sandbox Code Playgroud)

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

//Call
string messageFromClient = await ReceiveStringFromClientAsync();
Run Code Online (Sandbox Code Playgroud)

如果您仍然需要能够对这些方法进行同步调用,我建议创建后缀为“Async”的新方法。

现在,您需要在每个级别上执行此操作,直到到达网络调用,此时您将能够等待 .Net 的async方法。它们通常与其同步版本具有相同的名称,后缀为“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
}
Run Code Online (Sandbox Code Playgroud)