Reputation: 892
I'm attempting to create a helper method for my view models which takes a collection of actions to perform and will block other collection of actions occurring at the same time.
I believe that this is a prime candidate to use Task.ContinueWith
and my original hardcoded functionality worked without issue using this method but when I attempted to build the actions at runtime I'm hitting an issue in that my Tasks are running in different orders than I would expect.
My class looks like:
internal abstract class ViewModel : IViewModel
{
private readonly AutoResetEvent autoResetEvent = new AutoResetEvent(true);
protected Task SynchronousTask(IList<Action> actions)
{
var initialTask = new Task(() => this.autoResetEvent.WaitOne());
for (var i = 0; i < actions.Count; i++)
{
var i1 = i;
initialTask.ContinueWith(task => actions[i1]());
}
initialTask.ContinueWith(task => this.autoResetEvent.Set());
initialTask.Start();
return initialTask;
}
}
Essentially I want to wait on the AutoResetEvent
blocking any new tasks running until this has been set. I then loop through each of the tasks adding them as continuations of the initial task and finally ending it with a continuation to set the AutoResetEvent
.
In theory to me this looks like it should work, but when I run it I end up with the continuations running in the incorrect order.
I understand that there are other ways that I could do this without using the ContinueWith
s but I'd like to see where and how I went wrong using this method.
Upvotes: 1
Views: 882
Reputation: 3414
All you need is just chain to the last task, not initial task. Imagine hooking next task to the previous one, instead of hooking all tasks to the first one.
public static Task SynchronousTask(List<Action> actions)
{
var initialTask = new Task(() => autoResetEvent.WaitOne());
Task lastTask = initialTask;
for (int i = 0; i < actions.Count; i++)
{
int i1 = i;
lastTask = lastTask.ContinueWith(task => actions[i1]()); // here
}
lastTask.ContinueWith(task => autoResetEvent.Set());
initialTask.Start();
return initialTask;
}
Upvotes: 2
Reputation: 101553
When you add multiple continuations to the same task, like in your example - they are started in certain order (as far as I know, that order is not documented and you should not rely on it - but it seems they are started in last in first out order), but they do not run in particular order. After they are started - they all run in parallel. So in your case, all actions and autoResetEvent.Set()
will execute almost at the same time after initialTask
has completed. If you need sequential execution - just put all that code in one Task
and run it, no need to use ContinueWith
here. If you really want to use ContinueWith
- chain continuations (call ContinueWith
on result of previous ContinueWith
).
Upvotes: 5