Ожидание задач от нескольких объектов - PullRequest
0 голосов
/ 01 сентября 2018

У меня есть приложение, которое использует MEF для загрузки плагинов. Все эти плагины соответствуют следующему интерфейсу:

public interface IPlugin {
    Task Start();
}

Все методы реализованы как async: public async Task Start()

Когда приложение работает, у меня есть свойство IEnumerable<IPlugin>, доступное для всех плагинов. Вопрос в том, как я могу запустить все методы Start() параллельно и ждать, пока все методы не будут завершены?

Я знаю о Parallel.ForEach(plugins, plugin => plugin.Start()), но это не ожидаемо, и выполнение продолжается до запуска всех плагинов.

Наиболее многообещающим решением, похоже, является Task.WhenAll(), но я не знаю, как отправить в него неизвестный список методов без добавления каких-либо строительных лесов (что выглядит как накладные расходы).

Как мне это сделать?

Ответы [ 4 ]

0 голосов
/ 01 сентября 2018

Вы можете использовать Microsoft Reactive Framework, чтобы убедиться, что это ожидаемо, происходит асинхронно и параллельно.

await
    plugins
        .ToObservable()
        .SelectMany(plugin => Observable.FromAsync(() => plugin.Start()))
        .ToArray();
0 голосов
/ 01 сентября 2018

Как видите, 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.

0 голосов
/ 01 сентября 2018

А вот одна строка:

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

Плагины будут работать асинхронно, но не параллельно. Если по какой-то причине вы хотите явно отправить плагины в пул потоков, вы можете добавить Task.Run с async lambda в Select.

0 голосов
/ 01 сентября 2018

Вы можете сделать:

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