使用Await Async WhenAll与一次性对象

Chr*_*old 4 c# design-patterns idisposable async-await

在使用一次性对象时,我遇到了尝试并行处理多个任务(或运行时感觉合适)的问题.在下面的代码片段中,每个Processor对象在完成所需工作之前立即处理.

async public Task ProcessData(IEnumerable<int> data)
{
    var tasks = new List<Task>();

    foreach (var d in data)
    {
        using (var processor = new Processor(d))
        {
            processor.Completed += (sender, e) => { // Do something else };
            tasks.Add(processor.ProcessAsync());
        }
    }

    await Task.WhenAll(tasks);
}
Run Code Online (Sandbox Code Playgroud)

重写代码如下导致每个处理器执行其处理并且然后处理,但这不是运行不依赖于彼此的多个任务的最有效方式.

async public Task ProcessData(IEnumerable<int> data)
{
    foreach (var d in data)
    {
        using (var processor = new Processor(d))
        {
            processor.Completed += (sender, e) => { // Do something else };
            await processor.ProcessAsync();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

有人可以解释为什么第一个例子处理'早期'并给出这种情况的最佳代码模式的例子.

Ste*_*ary 7

它有助于await将当前方法视为暂停,即使它不会阻塞该线程.

在第一个示例中,当您执行foreach循环时,每次创建一个Processor,启动一个操作(将操作保存Task在列表中),然后处理Processor.你的后foreach循环结束,那么你(异步)等待所有操作完成.

在第二个示例中,当您执行foreach循环时,每次创建一个Processor,启动一个操作,(异步)等待它完成,然后处理Processor.

要解决这个问题,你应该编写一个帮助方法,如下:

private static async Task ProcessData(int data)
{
  using (var processor = new Processor(d))
  {
    processor.Completed += (sender, e) => { /* Do something else */ };
    await processor.ProcessAsync();
  }
}
Run Code Online (Sandbox Code Playgroud)

您的帮助程序方法定义了一个更高级别的操作,它将Processor在适当的时候处理自己的资源.然后您可以同时开始所有工作:

public async Task ProcessData(IEnumerable<int> data)
{
  ...
  await Task.WhenAll(data.Select(d => ProcessData(d)));
  ...
}
Run Code Online (Sandbox Code Playgroud)