【问题标题】:How to use Task.WhenAll() correctly如何正确使用 Task.WhenAll()
【发布时间】:2012-08-14 10:25:30
【问题描述】:

我正在尝试使用Task.WhenAll 来等待多个任务的完成。

我的代码如下 - 它应该启动多个异步任务,每个任务都会检索一条公交路线,然后将它们添加到本地数组中。但是,Task.WhenAll(...) 立即返回,并且本地路由数组的计数为零。这看起来很奇怪,因为我希望每个Task 中的各种await 语句意味着流被暂停,并且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());

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

【问题讨论】:

  • 你有没有试过检查最后的Task是不是没有故障状态?
  • 我认为在 foreach 循环中await dispatcher 可能有问题。 UI线程会立即观察并显示?
  • @ie。是的,它的状态是 RanToCompletion。数组中所有任务的状态也是 RanToCompletion,尽管当我实际单独检查它们时,每个任务的 Result 字段都是 WaitingForActivation
  • @user861114 不知道你的意思,UI 会观察和显示,但是'await' 关键字不应该意味着在方法完成之前不会返回执行吗?

标签: c# .net asynchronous .net-4.5 async-await


【解决方案1】:

这归结为对 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
}

感谢Dave Smits

【讨论】:

    【解决方案2】:

    我怀疑问题出在您致电Task.Factory.StartNew()。我怀疑你最终会得到一个Task&lt;Task&gt;,而你只是发现它何时有效地开始任务。

    试试这个:

    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);
    

    【讨论】:

    • 谢谢。我假设我的方法的其余部分将像以前一样继续,即将任务添加到任务数组中,然后在数组上调用 Task.WhenAll(...)。在这种情况下,这种方法与我的答案中的方法相比如何?有什么优势吗?
    • @CarlosP:老实说,我真的没有足够的上下文来评论哪种方法更好。但是,是的,您的方法的其余部分将像以前一样继续。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-11
    • 1970-01-01
    • 2015-01-30
    • 2014-09-08
    • 2015-09-10
    • 2023-03-26
    • 1970-01-01
    相关资源
    最近更新 更多