C# Task 的各种状态,及状态切换

2,030 阅读1分钟

Task有一个 TaskStatus类型 的实例属性Status,指示当前Task实例是何状态。 另外要注意,Task是一个链表的结构,记住这个链表,有助对Task进一步理解。 image.png 另外,task实例默认的m_taskScheduler(TaskScheduler类型)属性是new ThreadPoolTaskScheduler();。m_taskScheduler就是用来为Task做调度服务的。

1 “正常”执行一个Task

Created-WaitingToRun-Running-RanToCompletion

var sw = new Stopwatch();
var myTask = new Task(() => {
    var currentTime = sw.ElapsedMilliseconds;
    Console.WriteLine($"{currentTime,12}ms  myTask started");
    Thread.Sleep(TimeSpan.FromSeconds(3)); 
});

var statFlg = (TaskStatus)(-1);

sw.Start();
Task.Run(() =>
{
    while (true)
    {
        if (statFlg != myTask.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask),-12} Status: {myTask.Status,-12}");
            statFlg=myTask.Status;
        }
    }
});
Thread.Sleep(TimeSpan.FromSeconds(1));
myTask.Start();

执行结果
image.png
这里的myTask经历了Created、WaitingToRun、Running、RanToCompletion,4个状态。图中可以看到WaitingToRun转到Running并没有代码做显示状态切换,两个状态的时间间隔约 7 毫秒,相对较短。如果是Task.Run(()=>{});,那Created、WaitingToRun、Running三个状态凑到了一起,按顺序“立马”执行。

2 两个任务,并抛异常

WaitingForActivation,Faulted

var sw = new Stopwatch();
var myTask = new Task(() => {
    var currentTime = sw.ElapsedMilliseconds;
    Console.WriteLine($"{currentTime,12}ms  myTask started");
    Thread.Sleep(TimeSpan.FromSeconds(3)); 
});
var myTask2 = myTask.ContinueWith(tt => {
    Thread.Sleep(TimeSpan.FromSeconds(3));
    throw new Exception();
});

var statFlg = (TaskStatus)(-1);
var statFlg2 = (TaskStatus)(-1);

sw.Start();
Task.Run(() =>
{
    while (true)
    {
        if (statFlg != myTask.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask),-12} Status: {myTask.Status,-12}");
            statFlg=myTask.Status;
        }
    }
});
Task.Run(() =>
{
    while (true)
    {
        if (statFlg2 != myTask2.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask2),-12} Status: {myTask2.Status,-12}");
            statFlg2 = myTask2.Status;
        }
    }
});
Thread.Sleep(TimeSpan.FromSeconds(1));
myTask.Start();

image.png myTask2通过ContinueWith的防止追加到myTask之后,myTask开启,myTask2是WaitingFroActivation的状态了。task内抛出异常,则任务就被置为错误状态。

3 取消执行

Canceled

var tokenSource=new CancellationTokenSource();
var sw = new Stopwatch();
var myTask = new Task(() => {
    var currentTime = sw.ElapsedMilliseconds;
    Console.WriteLine($"{currentTime,12}ms  myTask started");
    while (true)
    {
        tokenSource.Token.ThrowIfCancellationRequested();
    }
    //throw new Exception();
}, tokenSource.Token);
var myTask2 = myTask.ContinueWith(tt => {
    Thread.Sleep(TimeSpan.FromSeconds(3));
    throw new Exception();
});

var statFlg = (TaskStatus)(-1);
var statFlg2 = (TaskStatus)(-1);

sw.Start();
Task.Run(() =>
{
    while (true)
    {
        if (statFlg != myTask.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask),-12} Status: {myTask.Status,-12}");
            statFlg=myTask.Status;
        }
    }
});
Task.Run(() =>
{
    while (true)
    {
        if (statFlg2 != myTask2.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask2),-12} Status: {myTask2.Status,-12}");
            statFlg2 = myTask2.Status;
        }
    }
});
Thread.Sleep(TimeSpan.FromSeconds(1));
myTask.Start();
Thread.Sleep(TimeSpan.FromSeconds(3));
tokenSource.Cancel();

image.png 要注意这里,tokenSource.Token.ThrowIfCancellationRequested();这样是取消线程操作,如果直接在任务里throw的话,那myTask就会被置为Fault。不过无论是那种方式停止myTask,ContinueWith的下一下线程都是会继续执行的。

4 等待子任务完成

WaitingForChildrenToComplete

var tokenSource=new CancellationTokenSource();
var sw = new Stopwatch();
var myTask = new Task(() => {
    var currentTime = sw.ElapsedMilliseconds;
    Console.WriteLine($"{currentTime,12}ms  myTask started");
    Task.Factory.StartNew(() =>
    {
        Console.WriteLine($"{currentTime,12}ms  In sub task.");
        Thread.Sleep(TimeSpan.FromSeconds(7));
    }, TaskCreationOptions.AttachedToParent);
}, tokenSource.Token);
var myTask2 = myTask.ContinueWith(tt => {
    Thread.Sleep(TimeSpan.FromSeconds(3));
    throw new Exception();
});

var statFlg = (TaskStatus)(-1);
var statFlg2 = (TaskStatus)(-1);

sw.Start();
Task.Run(() =>
{
    while (true)
    {
        if (statFlg != myTask.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask),-12} Status: {myTask.Status,-12}");
            statFlg=myTask.Status;
        }
    }
});
Task.Run(() =>
{
    while (true)
    {
        if (statFlg2 != myTask2.Status)
        {
            var currentTime = sw.ElapsedMilliseconds;
            Console.WriteLine($"{currentTime,12}ms  Task Name: {nameof(myTask2),-12} Status: {myTask2.Status,-12}");
            statFlg2 = myTask2.Status;
        }
    }
});
Thread.Sleep(TimeSpan.FromSeconds(1));
myTask.Start();
Thread.Sleep(TimeSpan.FromSeconds(3));
tokenSource.Cancel();

image.png

WaitingForChildrenToComplete状态的切换可能并不是和子线程执行状态那么同步。