带有ASync任务的C#ForEach循环&从属后期异步任务

时间:2017-09-19 21:50:34

标签: c# asynchronous async-await parallel-foreach

我无法正确构建最有效的方法来迭代从请求对象启动的几个异步任务,然后执行一些依赖于请求对象和第一个异步任务结果的其他异步任务。我正在AWS中运行C#lambda函数。我尝试过这样的模型(为了简洁起见,已经省略了错误处理等):

public async Task MyAsyncWrapper()
{
  List<Task> Tasks = new List<Task>();
  foreach (var Request in Requests) 
  {
    var Continuation = this.ExecuteAsync(Request).ContinueWith(async x => {
      var KeyValuePair<bool, string> Result = x.Result;
      if (Result.Key == true)
      {
        await this.DoSomethingElseAsync(Request.Id, Request.Name, Result.Value);
        Console.WriteLine("COMPLETED");
      }
    }

    Tasks.Add(Continuation);
  }

  Task.WaitAll(Tasks.ToArray());
}

这种方法导致DoSomethingElseAsync()方法没有真正等待,在我的很多Lambda函数调用中,我从未得到“COMPLETED”输出。我也用这种方法解决了这个问题:

public async Task MyAsyncWrapper()
{
  foreach (var Request in Requests) 
  {
    KeyValuePair<bool, string> Result = await this.ExecuteAsync(Request);

    if (Result.Key == true)
    {
      await this.DoSomethingElseAsync(Request.Id, Request.Name, Result.Value);
      Console.WriteLine("COMPLETED");
    }
  }
}

这很有效,但我认为这很浪费,因为我只能在等待asnyc完成时执行循环的一次迭代。我也引用了JavaFX entirely customized windows?,但问题是我基本上有两个循环,一个用于填充任务,另一个用于在完成后迭代它们,在那里我无法访问原始{{1}对象了。基本上就是这样:

Request

有关在foreach循环中实现此类异步链接的更好方法的任何想法吗?我理想的方法不是将请求对象作为List<Task<KeyValuePair<bool, string>>> Tasks = new List<Task<KeyValuePair<bool, string>>>(); foreach (var Request in Requests) { Tasks.Add(ths.ExecuteAsync(Request); } foreach (Task<KeyValuePair<bool, string>> ResultTask in Tasks.Interleaved()) { KeyValuePair<bool, string> Result = ResultTask.Result; //Can't access the original request for this method's parameters await this.DoSomethingElseAsync(???, ???, Result.Value); } 响应的一部分返回,所以我想尽可能尝试找其他选项。

2 个答案:

答案 0 :(得分:3)

考虑使用TPL数据流:

var a = new TransformBlock<Input, OutputA>(async Input i=>
{
    // do something async.
    return new OutputA();
});

var b = new TransformBlock<OutputA, OutputB>(async OutputA i =>
{
    // do more async.
    return new OutputB();
});

var c = new ActionBlock<OutputB>(async OutputB i =>
{
    // do some final async.
});

a.LinkTo(b, new DataflowLinkOptions { PropogateCompletion = true });
b.LinkTo(c, new DataflowLinkOptions { PropogateCompletion = true });

// push all of the items into the dataflow.
a.Post(new Input());
a.Complete();

// wait for it all to complete.
await c.Completion;

答案 1 :(得分:3)

我可能会误解,但为什么不移动你的#34;迭代&#34;进入它自己的函数,然后使用Task.WhenAll并行等待所有迭代。

public async Task MyAsyncWrapper()
{
  var allTasks = Requests.Select(ProcessRequest);

  await Task.WhenAll(allTasks);
}

private async Task ProcessRequest(Request request)
{
    KeyValuePair<bool, string> Result = await this.ExecuteAsync(request);

    if (Result.Key == true)
    {
      await this.DoSomethingElseAsync(request.Id, request.Name, Result.Value);
      Console.WriteLine("COMPLETED");
    }
}