【问题标题】:Async method within Task.Factory.StartNew wait all tasks before continuingTask.Factory.StartNew 中的异步方法在继续之前等待所有任务
【发布时间】:2018-05-29 18:21:36
【问题描述】:

我有以下代码:

public async Task<bool> ExecuteAsync()
{        
    return await someresult;
}


public async Task DoSomethingAsync()
{

   var tasks = new List<Task>();

   foreach (var item in someList)
   {

         var task = Task.Factory.StartNew(async () =>
                    {
                          await ExecuteAsync();
                    });

         tasks.Add(task);
   }


   await Task.WhenAll(tasks);

   // The program execution should stop here and next code should be run when all tasks are completed


}

基本上我在一个 foreach 循环中,我为每个项目启动一个新任务。

一旦所有任务都启动,我希望在所有任务完成后执行await Task.WhenAll(tasks); 之后的代码。当然所有都是异步的,因此我想知道这是否可能?因为所有都是异步的,所以执行不会在 Task.WhenAll(tasks) 处停止,而是会继续,即使并非所有任务都已完成。我该如何解决这个问题?

【问题讨论】:

  • 你为什么使用Task.Factory.StartNew?您是否停留在 .NET 4.0 上?如果没有,请改用Task.Run
  • 另外,async () =&gt; await ExecuteAsync() 没有意义,你必须使用Task.Run(() =&gt; ExecuteAsync)
  • StartNew 不直接支持异步 Lambadas。 task 的类型将是 Task&lt;Task&lt;bool&gt;&gt;,而 WhenAll 的行为将不符合您的预期。使用 Task.Run 或者您需要在将其添加到列表之前调用.Unwrap()。
  • @CamiloTerevinto:我的理解是您不应该在循环中运行 Task.Run。我错了吗?
  • @user2818430 Task.Run 在正常情况下优于 Task.Factory.StartNew

标签: c# async-await task multitasking


【解决方案1】:

首先,为什么要为循环中的每个任务浪费线程池,您的代码可以写成:

public  Task<bool> ExecuteAsync()
{
    return  ItReturnsATask();
}

public async Task DoSomethingAsync()
{

    var tasks = new List<Task>();

      foreach (var item in someList)
      {
        //ExecuteAsync can be replace by ItReturnsATask if there's no extra processing
            tasks.Add(ExecuteAsync());
      }

     await Task.WhenAll(tasks);
}

您的代码错过了异步的要点,它从线程池中创建了几个线程并使用它们,这些线程在这里浪费了。至于您的实际问题,await Task.WhenAll(tasks) 之后的任何内容都不会在所有任务完成之前执行(假设我们正在捕获当前上下文)。

【讨论】:

  • 您正在做出危险的假设,即此代码运行在无上下文的框架上,这可能是真的,也可能不是。在 WinForms、WPF、Xamarin 和 WebForms 中,使用线程池线程和不使用线程池线程之间存在巨大差异。
  • @CamiloTerevinto 如果ExecuteAsync是一个不需要捕获当前同步上下文的方法,那么它应该被写成不使用它,而不是简单地期望每个调用者使用它从上下文中调用它-免费线程。该方法在不需要(也不应该使用)时使用当前同步上下文将是所述方法中的错误。
  • @CamiloTerevinto 我的假设是基于这两篇文章Task.Run Etiquette and Proper Usage、Task.Run Etiquette Examples: Don't Use Task.Run in the Implementation,你能给我一篇关于这些巨大差异的文章吗?
  • @Servy 是的,它应该是。但事实上我们不知道ExecuteAsync 做了什么,也不知道在通话之前或之后会发生什么。因此,虽然一般建议是正确的,但我认为您不能如此轻松地拨打电话(例如,假设 ExecuteAsync 来自第三方)
  • @CamiloTerevinto 如果方法没有正确编写,并且实际上不应该使用当前同步上下文,那么应该更正该方法,而不是使用StartNew 或Run 在这里调用它。与其试图破解一个错误,不如修复错误,如果它甚至存在的话(你只是假设它确实存在)。
猜你喜欢
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 2018-04-19
  • 1970-01-01
  • 2016-09-23
  • 2020-03-20
  • 1970-01-01
相关资源
最近更新 更多