何时会发生任务内联?

5

在阅读了来自这里等来源中关于TPL内联的文章之后,我得到的印象是调用Task.Wait()会启动一个尚未开始的任务(至少使用默认的调度程序)。然而,编写一个快速演示如下:

var taskB = new Task(
  () =>
      {
        Console.WriteLine("In TaskB");
        System.Threading.Thread.Sleep(5000);
        Console.WriteLine("Leaving TaskB");
      });

var taskA = new Task(
  () =>
      {
        Console.WriteLine("In TaskA");
        System.Threading.Thread.Sleep(500);
        Console.WriteLine("Waiting on TaskB");
        taskB.Wait();
        Console.WriteLine("Leaving TaskA");
        });

taskA.Start();
taskA.Wait();

造成死锁。TaskA到达taskB.Wait()代码行,但taskB从未启动。我没有改变调度程序或其他任何东西,因此我不确定为什么对taskB的.Wait()调用不会导致它启动。


4
你的示例代码中没有调用taskB.Start()。 - Sean U
2个回答

15
Wait()不会使任务Start()。如果在未启动的任务上调用Wait(),它将等待任务开始并完成,直到完成、等待超时或取消等待。由于你对Wait()的调用没有包含取消标记或超时,因此任务需要无限期地完成等待。
我认为你从博客中感到困惑的是这句话:
“但是,如果它尚未开始执行,则Wait可能能够将目标任务从其排队的调度程序中提取出来,并在当前线程中内联执行它。”
关键在于短语“尚未开始执行”。这并不意味着没有调用Start(),而是Start()已被调用,它安排了该任务并使其准备好执行,但任务尚未开始执行。 Start()必须用于安排任务执行,它不会立即开始执行。这是该段内容的主要观点。如果任务已经准备好但未被安排,它可以被内联执行。但它不会启动一个甚至还没有被安排的任务。
如果查看MSDN(请参见这里)中的TaskStatus,您会看到以下值:
  • Created(已创建)
  • WaitingForActivation(等待被激活)
  • WaitingToRun(等待运行)
  • Running(运行中)
  • WaitingForChildrenToComplete(等待子任务完成)
  • RanToCompletion(已成功完成)
  • Canceled(已取消)
  • Faulted(已出错)

当您使用newfactory创建任务时,它处于Created状态。在此状态下,任务不会发生任何变化。一旦开始执行,它就会转移到WaitingForActivation等状态,直到达到Running状态。根据该博客文章的描述,在此期间,任务有可能被内联。

所以长话短说,创建任务只是将其置于Created状态,并且如果调用Wait()方法,则不会使其开始执行。明白了吗?


2
那篇文章提到的内联不同于启动任务。等待任务并不会启动它 - 这一点可以很容易地证明。只需尝试以下操作:
namespace Test
{
    using System;
    using System.Threading.Tasks;

    internal class TestCompile
    {
        private static void Main(string[] args)
        {
            Task t = new Task(() => Console.WriteLine("Executed!"));
            t.Wait(5000);
            Console.WriteLine("After wait...");
            Console.ReadKey();
        }
    }
}

你会发现任务从未开始...

调用task.Wait()不会启动一个任务。如果以下条件满足,它将导致任务立即在当前线程上“内联”执行(使用默认调度程序):

  • 任务已经启动,但是...
  • 任务目前没有在执行(因为任务被调度器排队)
  • 任务的调度程序(在构造时创建)与当前调度程序相同
  • 任务没有被取消
  • 任务没有故障

第三点比较模糊 - 它可以在不同的调度程序上进行内联执行,但这需要另一个调度程序能够立即执行它,因此它取决于调度程序是否支持此功能。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接