Correct approach to wait for multiple async methods to complete

I have an IWorkflow interface defined as follows:

public interface IWorkflow
{
}


And I have an Engine class:

public sealed class Engine : IEngine
{
private readonly List<IWorkflow> workflows = new List<IWorkflow>();

public Engine(IEnumerable<IWorkflow> workflows)
{
}

public void Start()
{
var configureTasks = this.workflows.Select(w => w.ConfigureAsync()).ToArray();

var startTasks = this.workflows.Select(w => w.StartAsync()).ToArray();
}

public void Stop()
{
var stopTasks = this.workflows.Select(w => w.StopAsync()).ToArray();
}
}


Is this the correct way for the Engine to invoke in parallel the configure method on all workflows and then once all are completed, invoke in parallel the start method on all workflows?

-
Is there any reason why Engine isn't also asynchronous? –  svick Dec 4 '12 at 20:06
I don't see a need for it to be as there is only one Engine and it starts on the app thread. I'm updating an old .NET 2.0 app so I'm just getting to grips with Tasks and async/await & wanted to confirm I was using it in the correct way. –  Trevor Pilley Dec 5 '12 at 8:53
If you mean it's a GUI app, then I think not locking up the GUI while the engine starts or stops is a good reason for that. –  svick Dec 5 '12 at 8:55
It's a console app running in TopShelf –  Trevor Pilley Dec 5 '12 at 9:06

Your code is absolutely correct in case when you want to start workflows only when all of them are configured.

But if you want to start each workflow once it's configured (independently from other workflows) then it might be a good idea to use continuations... In .NET 4.5 it would look like this:

public sealed class Engine : IEngine
{

public Engine(IEnumerable<IWorkflow> workflows)
{
_workflows = new List<IWorkflow>(workflows);
}

{
await workflow.ConfigureAsync();
await workflow.StartAsync();
}

public void Start()
{
}

public void Stop()
{
var stopTasks = _workflows.Select(w => w.StopAsync()).ToArray();
}
}


Also I would suggest to use CancellationToken to stop asynchronous processing.

UPDATE Based on comments it is really needed to wait for all workflows to be configured before starting them. So cancellable implementation can look like this:

public interface IWorkflow
{
}

public sealed class Engine : IEngine
{

public Engine(IEnumerable<IWorkflow> workflows)
{
_workflows = new List<IWorkflow>(workflows);
_cancellationTokenSource = new CancellationTokenSource();
}

{
if (_cancellationTokenSource.IsCancellationRequested)
return;
}

public void Start()
{
}

public void Stop()
{
_cancellationTokenSource.Cancel();

I think your suggestion to use CancellationToken is not right. Cancellation is something exceptional (and awaiting a canceled Task throws an exception). Stopping some long-running process is not exceptional. –  svick Dec 4 '12 at 20:09
@svick CancellationToken is a recommended way to stop asynchronous processing, it does not throw exceptions and is not something exceptional. You probably confuse it with Thread.Abort, CancellationToken.ThrowIfCancellationRequested or Task.Wait(CancellationToken) which do throw exceptions. See the link I've included in my post for example of usage. –  almaz Dec 4 '12 at 20:47
But ThrowIfCancellationRequested() is the recommended way of using CancellationToken, AFAIK. Also, Task has a special status Canceled and when you Wait() or await such Task, it will throw an exception. –  svick Dec 4 '12 at 22:55