等待来自多个对象的任务

Awaiting tasks from multiple objects

我有一个使用 MEF 加载插件的应用程序。所有这些插件都符合以下接口:

public interface IPlugin {
    Task Start();
}

所有方法实现为asyncpublic async Task Start()

当应用程序 运行ning 时,我有一个 IEnumerable<IPlugin> 属性 可用于所有插件。问题基本上是我如何 运行 所有 Start() 方法并行并等待所有方法完成?

我知道 Parallel.ForEach(plugins, plugin => plugin.Start()),但这是不可等待的,在所有插件启动之前继续执行。

最有前途的解决方案似乎是 Task.WhenAll(),但我不知道如何在不添加一些脚手架的情况下将未知的方法列表发送到其中(这似乎是开销)。

我怎样才能做到这一点?

你可以这样做:

var tasks = new List<Task>();
foreach(var plugin in plugins) 
{
   var task = plugin.Start();
   tasks.Add(task);
}
await Task.WhenAll(tasks); 

如您所见Start方法returns一个Task。我会定义一个插件加载任务列表,并在每个任务完成时用 Task.WhenAll 检查。之后您可以假设所有 Start 方法都已返回。

List<IPlugin> plugins = ... 
var pluginsLoadingTasks = new List<Task>();

foreach(var plugin in plugins)
{
    pluginsLoadingTasks.Add(plugin.Start());
}

// It's not necessary to check if pluginsLoadingTasks is empty, 
// because WhenAll won't throw an exception in that case
await Task.WhenAll(pluginsLoadingTasks);
// You can assume all Start methods have completed

我建议你 to read the differencesTask.WhenAllParallel.ForEach 结构之间。

这是单行代码:

await Task.WhenAll(plugins.Select(p => p.Start()));

插件将 运行 异步,但不是并行。如果你出于某种原因想要明确地将插件分派到线程池,你可以将 Task.Runasync lambda 添加到 Select.

您可以使用 Microsoft 的 Reactive Framework 来确保这是可等待的、异步和并行发生的。

await
    plugins
        .ToObservable()
        .SelectMany(plugin => Observable.FromAsync(() => plugin.Start()))
        .ToArray();