You can use TaskFactory.ContinueWhenAll to build a continuation that runs when the input tasks all complete.
Task[] tasks = new Task[3]
{
LoadTools(),
LoadResources(),
LoadPersonel()
};
Task.Factory.ContinueWhenAll(tasks, t =>
{
DataView = CollectionViewSource.GetDefaultView(Data);
DataView.Filter = FilterTimelineData;
IsBusy = false;
}, CancellationToken.None, TaskContinuationOptions.None,
TaskScheduler.FromCurrentSynchronizationContext());
Note that this becomes simpler if you use C# 5's await
/async
syntax:
private async void LoadData()
{
SetBusy("Loading...");
Data.Clear();
Task[] tasks = new Task[3]
{
LoadTools(),
LoadResources(),
LoadPersonel()
};
await Task.WhenAll(tasks);
DataView = CollectionViewSource.GetDefaultView(Data);
DataView.Filter = FilterTimelineData;
IsBusy = false;
}
However if I return the continuation task from the service method the continuation never gets called and WaitAll waits forever
The problem is that your continuation task requires the UI thread, and you're blocking the UI thread in the WaitAll
call. This creates a deadlock which will not resolve.
Fixing the above should correct this - you'll want to return the Continuation as the Task, as that's what you need to wait for completion - but by using TaskFactory.ContinueWhenAll
you free up the UI thread so it can process those continuations.
Note that this is another thing that gets simplified with C# 5. You can write your other methods as:
internal static async Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback)
{
try
{
await Task.Run(() =>
{
//... get resources from somewhere
return resources;
});
}
catch (Exception e)
{
errorCallback(task.Exception);
}
completedCallback(task.Result);
}
That being said, it's typically better to write the methods to return a Task<T>
instead of providing callbacks, as that simplifies both ends of the usage.