【问题标题】:Why Task.Factory.StartNew returns immediately while Task.Run does not?为什么 Task.Factory.StartNew 立即返回而 Task.Run 没有?
【发布时间】:2023-03-31 00:57:01
【问题描述】:

考虑一下这段代码sn-p:

Task[] tasks = new Task[4];
for (var i = 0; i < tasks.Length; ++i) {
    tasks[i] = Task.Run(async () =>
    {
        await Task.Delay(4000);
    });
}
for (var i = 0; i < tasks.Length; ++i)
    await tasks[i];

Console.WriteLine("Done!");

这按预期工作,执行需要 4.000 毫秒。但是,如果我将 Task.RunTask.Factory.StartNew 交换,则只需 0.006 毫秒!

谁能解释一下原因?

【问题讨论】:

    标签: c# task-parallel-library


    【解决方案1】:

    谁能解释一下原因?

    简单地说,StartNew 不理解 async 代表。

    所以,when your delegate returns an incomplete task at its first awaitStartNew 看到了委托出口并认为其工作已完成。这就是为什么它在这里返回Task&lt;Task&gt;Task.Run 具有处理异步委托的特殊逻辑,自动解包内部任务。

    这只是将StartNew 与异步代码一起使用的陷阱之一;我在我的博文StartNew is Dangerous 中详细介绍了这一点和其他内容。

    【讨论】:

      【解决方案2】:

      好的,我自己看完https://blogs.msdn.microsoft.com/pfxteam/2011/10/24/task-run-vs-task-factory-startnew/找到了答案

      通过在这里使用 async 关键字,编译器将映射这个 委托为Func&lt;Task&lt;int&gt;&gt;:调用委托将返回 Task&lt;int&gt; 表示此调用的最终完成。和 因为代表是Func&lt;Task&lt;int&gt;&gt;TResultTask&lt;int&gt;,因此 “t”的类型将是Task&lt;Task&lt;int&gt;&gt;,而不是Task&lt;int&gt;

      所以这段代码按预期工作:

      Task[] tasks = new Task[4];
      for (var i = 0; i < tasks.Length; ++i) {
          tasks[i] = Task.Factory.StartNew(async () =>
          {
              await Task.Delay(4000);
          });
      }
      for (var i = 0; i < tasks.Length; ++i)
          await await (tasks[i] as Task<Task>);
      
      Console.WriteLine("Done!");
      

      这也可以使用Unwrap实现:

      Task[] tasks = new Task[4];
      for (var i = 0; i < tasks.Length; ++i) {
          tasks[i] = Task.Factory.StartNew(async () =>
          {
              await Task.Delay(4000);
          }).Unwrap();
      }
      for (var i = 0; i < tasks.Length; ++i)
          await tasks[i];
      
      Console.WriteLine("Done!");
      

      【讨论】:

        【解决方案3】:

        答案是here

        这两种方法在行为方面存在差异: Task.Run(Action) 默认情况下不允许子任务以 TaskCreationOptions.AttachedToParent 选项附加到 当前任务实例,而 StartNew(Action) 确实

        因此,Task.Run 将等待执行将完成,Task.Factory.StartNew 立即返回任务。

        【讨论】:

          猜你喜欢
          • 2016-11-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-01-14
          • 1970-01-01
          • 2012-07-11
          • 1970-01-01
          相关资源
          最近更新 更多