5

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

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);
}

如下重写代码会导致每个处理器执行其处理然后被处置,但这不是运行多个不相互依赖的任务的最有效方式。

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();
        }
    }
}

有人可以解释为什么第一个示例是“早期”处理,并举例说明这种情况下的最佳代码模式。

4

2 回答 2

8

它有助于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();
  }
}

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

public async Task ProcessData(IEnumerable<int> data)
{
  ...
  await Task.WhenAll(data.Select(d => ProcessData(d)));
  ...
}
于 2013-07-22T21:23:40.313 回答
2

尽管 Stephen Cleary 的回答很优雅,并且在这种情况下可能是更好的选择,但我认为值得提请注意作为 Reactive Extensions (Rx) 的一部分在类似情况下可能有用的东西。它提供了一堆与 相关的助手IDisposable,这将使您能够做到这一点:

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

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

要获得CompositeDisposable,您需要 NuGet 对Rx-Main.

我不认为在这种情况下我会使用它,但我想我会尽量避免到达我需要看起来像这样的代码的地方。既有 aTask代表您正在完成的工作Processor,也有一个事件来表明......某事?......它已经完成......?那么Task可以为你做到这一点,那么为什么要两者兼而有之呢?而且事件相对混乱——我总是发现,无论是 RxIObservable<T>还是直接使用Task都是比依赖事件更好的解决方案。

于 2013-07-23T13:45:03.957 回答