如何正确使用Task.WhenAll()

Car*_*s P 7 .net c# asynchronous async-await .net-4.5

我试图用来Task.WhenAll等待完成多个任务.

我的代码如下 - 它应该启动多个异步任务,每个异步任务检索一个总线路由,然后将它们添加到本地阵列.但是,Task.WhenAll(...)立即返回,并且本地路由数组的计数为零.这看起来很奇怪,因为我希望await每个语句中的各种语句Task意味着流程被暂停,并且在Task它完成之前不会返回.

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());
Run Code Online (Sandbox Code Playgroud)

我显然做错了什么 - 但是什么?

Car*_*s P 7

这归结于对async-await如何真正起作用的基本缺乏理解.

内部任务是将流返回到外部任务,然后在等待返回之前完成.

为了实现我想要的,我需要重构如下:

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
}
Run Code Online (Sandbox Code Playgroud)

感谢Dave Smits


Jon*_*eet 5

我怀疑问题是你的呼唤Task.Factory.StartNew().我怀疑你最终会得到一个Task<Task>,而你只是在发现它有效地开始了这项任务.

试试这个:

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);
Run Code Online (Sandbox Code Playgroud)