How to use Task.WhenAll() correctly
Solution 1
This was down to a basic lack of understanding of how async-await really works.
The inner task was returning flow to the outer task, which then finished before the await ever returned.
To achieve what I wanted, I needed to refactor as follows:
List<Task<BusRoute>> routeRetrievalTasks = new List<Task<BusRoute>>();
foreach (BusRouteIdentifier bri in stop.services)
{
BusRouteRequest req = new BusRouteRequest(bri.id);
routeRetrievalTasks.Add(BusDataProviderManager.DataProvider.DataBroker.getRoute(req));
}
foreach (var task in routeRetrievalTasks)
{
var route = await task;
this.routes.Add(route); // triggers events
}
Thanks to Dave Smits
Solution 2
I suspect the problem is your call to Task.Factory.StartNew()
. I suspect you're ending up with a Task<Task>
, and you're only finding out when it's effectively started the task.
Try this instead:
Func<Task> taskFunc = async () =>
{
var route = await BusDataProviderManager.DataProvider.DataBroker.getRoute(req);
// Add the route to our array (on UI thread as it's observed)
await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate
{
this.routes.Add(route);
});
}
Task getRouteTask = Task.Run(taskFunc);
Related videos on Youtube
Carlos P
Coder and mobile app developer (e.g. www.buschecker.com): C++, C# .NET, Silverlight, Objective C, Java, HTML & XAML. SOreadytohelp
Updated on June 04, 2022Comments
-
Carlos P almost 2 years
I am trying to use
Task.WhenAll
to await completion of multiple tasks.My code is below - it is supposed to launch multiple async tasks, each of which retrieves a bus route and then adds them to a local array. However,
Task.WhenAll(...)
returns immediately, and the count of the local routes array is zero. This seems strange, since I would expect the variousawait
statements within eachTask
to mean that the flow is suspended, and theTask
does not return until it's finished.List<Task> monitoredTasks = new List<Task>(); foreach (BusRouteIdentifier bri in stop.services) { BusRouteRequest req = new BusRouteRequest(bri.id); // Start a new task to fetch the route for each stop Task getRouteTask = Task.Factory.StartNew(async () => { var route = await BusDataProviderManager.DataProvider.DataBroker.getRoute(req); // Add the route to our array (on UI thread as it's observed) await dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, delegate { this.routes.Add(route); }); }); // Store the task in our monitoring list monitoredTasks .Add(getRouteTask); } Debug.WriteLine("Awaiting WHENALL"); await Task.WhenAll(monitoredTasks ); Debug.WriteLine(string.Format("WHENALL returned (routes count is {0} ", this.routes.Count)); this.OnWillEndFetchingRoutes(new EventArgs());
I'm obviously doing something wrong - but what?
-
Carlos P over 11 yearsThanks. I assume that the rest of my method would continue as before, i.e. add the Task to the array of tasks, then call Task.WhenAll(...) on the array. In which case, how does this approach compare to the one in my Answer? Are there any advantages?
-
Jon Skeet over 11 years@CarlosP: To be honest I don't really have enough context to comment on which is a better approach. But yes, the rest of your method would continue as before.