我有一个使用MEF加载插件的应用程序。所有这些插件均符合以下接口:
public interface IPlugin {
Task Start();
}
所有方法都以async
的形式实现:public async Task Start()
当应用程序运行时,所有插件都具有IEnumerable<IPlugin>
属性。问题基本上是我如何可以并行运行所有Start()
方法并等待所有方法完成?
我了解Parallel.ForEach(plugins, plugin => plugin.Start())
,但这是无法等待的,并且在所有插件启动之前,执行将继续。
最有前途的解决方案似乎是Task.WhenAll()
,但我不知道如何在不添加一些脚手架的情况下向其中发送未知方法列表。
我该怎么做?
答案 0 :(得分:6)
这是一个单线纸:
await Task.WhenAll(plugins.Select(p => p.Start()));
这些插件将异步运行,但不能并行运行。如果出于某种原因想要将插件显式分配到线程池,则可以将Task.Run
和async
lambda添加到Select
中。
答案 1 :(得分:2)
您可以这样做:
var tasks = new List<Task>();
foreach(var plugin in plugins)
{
var task = plugin.Start();
tasks.Add(task);
}
await Task.WhenAll(tasks);
答案 2 :(得分:2)
您可以使用Microsoft的Reactive Framework来确保这是可以等待的,异步且并行发生。
await
plugins
.ToObservable()
.SelectMany(plugin => Observable.FromAsync(() => plugin.Start()))
.ToArray();
答案 3 :(得分:0)
如您所见,Start
方法返回一个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
我建议您在Task.WhenAll
和Parallel.ForEach
结构之间to read the differences。