• .NET 中 如果一个Task A正在await另一个Task B,那么Task A是什么状态


    新建一个.NET Core控制台程序,输入如下代码:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            //使用Task.Run返回outer task,然后在Task.Run里面启动inner task,注意这里的Task.Run实际上是调用的public static Task Run(Func<Task> function)重载方法
            var outerTask = Task.Run(async () =>
            {
                //await 之前执行
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task began!");//注意这里的Task ID为1
    
                //启动inner task
                var innerTask = Task.Run(() =>
                {
                    //inner task 的线程沉睡5秒
                    Thread.Sleep(5000);
                });
    
                //输出outer task即将开始await
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will await!");
                await innerTask;//在outerTask的线程中await innerTask的Task线程
    
                //await 完成之后执行,从下面的输出可以看到,await之后的代码是使用一个新的线程来执行的,因为下面的Thread ID和await之前发生了变化。另外可以看到这里的Task ID为null,说明已经不是await之前的Task在执行这里的代码了
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will be finished!");
            });
    
            Thread.Sleep(1000);//主线程沉睡1秒,之后outer task的线程肯定开始执行了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//检查outer task的状态,可以看到此时状态为WaitingForActivation。注意这里的Task ID为2,说明上面Task.Run返回的outerTask只是个代理,并不是真正执行Task.Run参数中委托代码的Task
            Thread.Sleep(6000);//沉睡6秒,之后outer task和inner task的线程肯定结束执行了,也就是说outerTask和innerTask应该都结束了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//再次检查outer task的状态,可以看此时状态为RanToCompletion
    
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press key contiune...");
            Console.ReadKey();
        }
    }

    执行结果如下:

    上面代码中Task.Run是调用的public static Task Run(Func<Task> function)重载方法,可以看到Task.Run返回的outerTask其Task ID为2,但是执行Task.Run参数中委托代码的Task ID为1,说明Task.Run返回的outerTask只是个代理,并不是真正执行Task.Run参数中委托代码的Task

    接下来我们改用Task.Run的另一个重载方法public static Task Run(Action action)来执行同样的代码:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            //使用Task.Run返回outer task,然后在Task.Run里面启动inner task,注意这里的Task.Run是调用的public static Task Run(Action action)重载方法
            var outerTask = Task.Run(new Action(async () =>
            {
                //await 之前执行
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task began!");//注意这里的Task ID为1
    
                //启动inner task
                var innerTask = Task.Run(() =>
                {
                    //inner task 的线程沉睡5秒
                    Thread.Sleep(5000);
                });
    
                //输出outer task即将开始await
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will await!");
                await innerTask;//在outerTask的线程中await innerTask的Task线程
    
                //await 完成之后执行,从下面的输出可以看到,await之后的代码是使用一个新的线程来执行的,因为下面的Thread ID和await之前发生了变化。另外可以看到这里的Task ID为null,说明已经不是await之前的Task在执行这里的代码了
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will be finished!");
            }));
    
            Thread.Sleep(1000);//主线程沉睡1秒,之后outer task的线程肯定开始执行了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//检查outer task的状态,可以看到此时状态为RanToCompletion。注意这里的Task ID为1,说明outerTask和执行上面Task.Run参数中委托代码的Task是同一个Task
            Thread.Sleep(6000);//沉睡6秒,之后outer task和inner task的线程肯定结束执行了,也就是说outerTask和innerTask应该都结束了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//再次检查outer task的状态,可以看此时状态为RanToCompletion
    
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press key contiune...");
            Console.ReadKey();
        }
    }

    执行结果如下:

    这次上面代码中Task.Run是调用的public static Task Run(Action action)重载方法,但是结果和调用public static Task Run(Func<Task> function)重载方法时完全不一样了,这次Task.Run返回的outerTask和执行Task.Run参数中委托代码的Task,Task ID都为1,是同一个Task,不过outerTask在await后就直接结束了,因为其状态已经是RanToCompletion。

    由此可以看到Task.Run的两个重载方法public static Task Run(Func<Task> function)public static Task Run(Action action)返回的outerTask是不一样的。

    然后如果我们不用Task.Run,而是用new Task创建一个新的outerTask,然后启动outerTask,代码如下:

    using System;
    using System.Threading;
    using System.Threading.Tasks;
    
    class Program
    {
        static void Main(string[] args)
        {
            //声明outerTask
            Task outerTask = null;
    
            //新建一个Task对象然后赋值给outerTask,然后在outerTask里面启动inner task
            outerTask = new Task(async () =>
            {
                //await 之前执行
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task began!");//注意这里的Task ID为1
    
                //启动inner task
                var innerTask = Task.Run(() =>
                {
                    //inner task 的线程沉睡5秒
                    Thread.Sleep(5000);
                });
    
                //输出outer task即将开始await
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will await!");
                await innerTask;//在outerTask的线程中await innerTask的Task线程
    
                //await 完成之后执行,从下面的输出可以看到,await之后的代码是使用一个新的线程来执行的,因为下面的Thread ID和await之前发生了变化。另外可以看到这里的Task ID为null,说明已经不是await之前的Task在执行这里的代码了
                Console.WriteLine("Task ID " + (Task.CurrentId == null ? "" : Task.CurrentId.ToString()) + ", Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer task will be finished!");
            });
    
            //启动outerTask
            outerTask.Start();
    
            Thread.Sleep(1000);//主线程沉睡1秒,之后outer task的线程肯定开始执行了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//检查outer task的状态,可以看到此时状态为RanToCompletion。注意这里的Task ID为1,说明outerTask和执行上面new Task参数中委托代码的Task是同一个Task
            Thread.Sleep(6000);//沉睡6秒,之后outer task和inner task的线程肯定结束执行了,也就是说outerTask和innerTask应该都结束了
            Console.WriteLine($"Outer task ID {outerTask.Id.ToString()}, Outer task status:{outerTask.Status.ToString()}");//再次检查outer task的状态,可以看此时状态为RanToCompletion
    
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press key contiune...");
            Console.ReadKey();
        }
    }

    执行结果如下:

    可以看到这次结果和调用Task.Run的重载方法public static Task Run(Action action)时相同,同样这次outerTask和执行new Task参数中委托代码的Task,Task ID都为1,是同一个Task,outerTask仍然是在await后就直接结束了,因为其状态已经是RanToCompletion

    所以由此可见,使用Task.Run的重载方法public static Task Run(Func<Task> function)返回的outerTask是最靠谱的,因为该重载方法返回的outerTask是在Task.Run参数中委托代码全都执行完毕后才变为RanToCompletion状态的,但是Task.Run重载方法public static Task Run(Action action)和new Task返回的outerTask,在Task.Run和new Task参数中委托代码执行了await后就立马变为RanToCompletion状态了,这明显和预期不符,特别是在调用Task.Wait()方法时会容易造成Bug,因为Task.Wait()在Task状态为RanToCompletion时就跳出了,这是极其危险的,因为此时outerTask(Task.Run重载方法public static Task Run(Action action)和new Task返回的outerTask)中的代码实际上并没有执行完毕还在await中。

    此外我们还可以更改上面代码中的outerTask为一个后台线程outerThread来做同样的测试,代码如下:

    class Program
    {
        //定义outer thread为Program类的静态变量,这样我们在其执行方法OuterThreadRun中也可以检查outer thread的状态
        static Thread outerThread;
    
        //这是outer thread的执行方法,其在outer thread里面启动inner task
        static async void OuterThreadRun()
        {
            //await 之前执行
            Console.WriteLine("Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer thread began!");
    
            //显示在执行中的outer thread的状态,此时状态显示为Background,因为outer thread为后台线程,所以Background表示后台线程正在执行
            Console.WriteLine($"Thread ID { Thread.CurrentThread.ManagedThreadId.ToString() } : Outer thread status:{outerThread.ThreadState.ToString()}");
    
            //启动inner task
            var innerTask = Task.Run(() =>
            {
                //inner task 的线程沉睡5秒
                Thread.Sleep(5000);
            });
    
            //输出outer thread即将开始await
            Console.WriteLine("Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer thread will await!");
            await innerTask;//在outer thread线程中await innerTask的Task线程
    
            //await 完成之后执行,从下面的输出可以看到,await之后的代码是使用一个新的线程来执行的,因为下面的Thread ID和await之前发生了变化
            Console.WriteLine("Thread ID " + Thread.CurrentThread.ManagedThreadId.ToString() + " : Outer thread will be finished!");
        }
    
        static void Main(string[] args)
        {
            //初始化outer thread,绑定执行方法为OuterThreadRun,并设置IsBackground为true,即后台线程
            outerThread = new Thread(new ThreadStart(OuterThreadRun))
            {
                IsBackground = true
            };
    
            //开始执行outer thread
            outerThread.Start();
    
            Thread.Sleep(1000);//主线程沉睡1秒,之后outer thread线程肯定开始执行了
            Console.WriteLine($"Outer thread status:{outerThread.ThreadState.ToString()}");//检查outer thread的状态,可以看到此时状态为Stopped,因为现在outer thread正在await innerTask,这说明await中的线程状态是Stopped
            Thread.Sleep(6000);//沉睡6秒,之后outer thread线程和inner task的线程肯定结束执行了,也就是说outerThread和innerTask应该都结束了
            Console.WriteLine($"Outer thread status:{outerThread.ThreadState.ToString()}");//再次检查outer thread的状态,可以看此时状态为Stopped,因为outerThread和innerTask都结束运行了
    
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Press key contiune...");
            Console.ReadKey();
        }
    }

    结果如下所示:

  • 相关阅读:
    解决ubuntu中zip解压的中文乱码问题
    GCC 静态库和动态库
    交互式shell和非交互式shell、登录shell和非登录shell的区别
    牛顿迭代法实现平方根函数sqrt
    Sqrt函数高效实现
    Http、tcp、Socket连接区别
    C++11的一般概念——The C++ standard library, 2nd Edition 笔记(二)
    C++11新特性——The C++ standard library, 2nd Edition 笔记(一)
    【Java线程与内存分析工具】VisualVM与MAT简明教程
    Java设计模式:Proxy(代理)模式
  • 原文地址:https://www.cnblogs.com/OpenCoder/p/9794938.html
Copyright © 2020-2023  润新知