2

I have an application that uses MEF to load plugins. All of these plugins conform to the following interface:

public interface IPlugin {
    Task Start();
}

All the methods are implemented as async: public async Task Start()

When the application is running I have an IEnumerable<IPlugin> property available with all the plugins. The question is basically how I can run all the Start() methods in parallel and wait until all methods are finished?

I know about Parallel.ForEach(plugins, plugin => plugin.Start()), but this is not awaitable and execution continues before all plugins are started.

The most promising solution seems to be Task.WhenAll(), but I don't know how to send in an unknown list of methods into this without adding some scaffolding (which seems like overhead).

How can I accomplish this?

4 Answers 4

5

And here's a one-liner:

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

The plugins will run asynchronously, but not in parallel. If you want for some reason to dispatch plugins to thread pool explicitly, you may add Task.Run with async lambda into Select.

Sign up to request clarification or add additional context in comments.

8 Comments

Son of a bench, it's been staring me in the face this whole time! Would you care to elaborate on "The plugins will run asynchronously, but not in parallel" ?
You can output CurrentThread's id and you will see that plugins at least start on a single thread. Here's a nice article about it: blogs.msdn.microsoft.com/benwilli/2015/09/10/…
@Sergey.quixoticaxis.Ivanov For running in parallel, might it not be better to use AsParallel() (as in plugins.AsParallel().Select(p => p.Start())) instead of Task.Run? See here.
@pere57 I doubt it will make any difference. Also I'm not certain that plugins can get any value out of work "chunking" inside parallel query. It should be measured irl, what is better for this case.
"The plugins will run asynchronously, but not in parallel." -- This is not correct. Assuming that the Start method returns an incomplete task, in other words that it is truly asynchronous, which is a reasonable assumption to make, the plugins will run concurrently. Multiple asynchronous operations will be in-flight at the same time.
|
3

You can do:

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

5 Comments

My pet peeve is if(collection.Count > 0) - use if(collection.Any()). You don't care how many items, just 'is there any?'.
@Neil, very good suggestion! I always keep forgetting that Any() is more efficient in this case. :)
Or you can ommit Any check as WhenAll can work with zero tasks.
Also you probably meant Task.WhenAll, not Tasks.WhenAll
@Sergey.quixoticaxis.Ivanov, I didn't know you could pass an empty collection to it, so thanks for the suggestion.
2

You could use Microsoft's Reactive Framework to ensure that this is awaitable, happens asynchronously and in parallel.

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

Comments

0

As you can see Start method returns a Task. I would define a list of plugin loading tasks and check with Task.WhenAll when every task is completed. After that you can assume all Start methods have returned.

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

I suggest you to read the differences between Task.WhenAll and Parallel.ForEach constructs.

1 Comment

How is your answer different from what Frank suggested?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.