.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();
}); //输出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();//主线程沉睡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();//沉睡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();
}); //输出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();//主线程沉睡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();//沉睡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();
}); //输出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();//主线程沉睡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();//沉睡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();
}); //输出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();//主线程沉睡1秒,之后outer thread线程肯定开始执行了
Console.WriteLine($"Outer thread status:{outerThread.ThreadState.ToString()}");//检查outer thread的状态,可以看到此时状态为Stopped,因为现在outer thread正在await innerTask,这说明await中的线程状态是Stopped
Thread.Sleep();//沉睡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();
}
}
结果如下所示:

.NET 中 如果一个Task A正在await另一个Task B,那么Task A是什么状态的更多相关文章
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- C# Task中的Func, Action, Async与Await的使用
在说Asnc和Await之前,先说明一下Func和Action委托, Task任务的基础的用法 1. Func Func是一种委托,这是在3.5里面新增的,2.0里面我们使用委托是用Delegate, ...
- 浅析C#中的Thread ThreadPool Task和async/await
.net 项目中不可避免地要与线程打交道,目的都是实现异步.并发.从最开始的new Thread()入门,到后来的Task.Run(),如今在使用async/await的时候却有很多疑问. 先来看一段 ...
- C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- C#中 Thread,Task,Async/Await 异步编程
什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...
- 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿
说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...
- PySpark 的背后原理--在Driver端,通过Py4j实现在Python中调用Java的方法.pyspark.executor 端一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。
PySpark 的背后原理 Spark主要是由Scala语言开发,为了方便和其他系统集成而不引入scala相关依赖,部分实现使用Java语言开发,例如External Shuffle Service等 ...
- 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读
记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...
- C#多线程和异步(二)——Task和async/await详解
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
随机推荐
- python 之 os._exit() sys.exit() 、exit()
sys.exit 执行该语句会直接退出程序,这也是经常使用的方法,也不需要考虑平台等因素的影响,一般是退出Python程序的首选方法. 退出程序引发SystemExit异常,(这是唯一一个不会被认为是 ...
- centos自带python2.6升级到python2.7。并解决yum pip easy_install pip等模块兼容性问题
参考原文: https://www.cnblogs.com/kimyeee/p/7250560.html https://www.cnblogs.com/galaxy-gao/p/5796488 ...
- 重构指南 - 为布尔方法命名(Rename boolean method)
如果一个方法中包含多个布尔类型的参数,一是方法不容易理解,二是调用时容易出错. 重构前代码 public class BankAccount { public void CreateAccount(C ...
- websocket 和 dwr 做web端即时通信
一.WebSocket是HTML5出的东西(协议),也就是说HTTP协议没有变化,或者说没关系,但HTTP是不支持持久连接的(长连接,循环连接的不算) 首先HTTP有1.1和1.0之说,也就是所谓的k ...
- php接收post过来的json数据
<html> <head> <title>json</title> <script src="//cdn.bootcss.com/jqu ...
- 仿照jQuery进行一些简单的框架封装(欢迎指教~)
(function(window,undefined){ var arr = [], push = arr.push, slice = arr.slice; //首先要做的就是封装一个parseHtm ...
- 在 Linux 中使用 Azure Premium 存储的基本优化指南
Note 以下测试和结果都是基于 CentOS 6.5.对于其他版本,请参考本文档,并自行进行相关测试. 建议使用最新的内核版本 一般情况下,新的内核版本能解决老版本中存在的问题,添加对新出现硬件的支 ...
- Linux->ZooKeeper开机启动的俩种方式
两种方式可以实现开机自启动 第一种:直接修改/etc/rc.d/rc.local文件 在/etc/rc.d/rc.local文件中需要输入两行, 其中export JAVA_HOME=/usr/jav ...
- Qt::FocusPolicy的使用
http://blog.csdn.net/imxiangzi/article/details/50742813
- 网页入口ControlServlet分析
init() configureBsf(); //配置自定义bsf,即在bean script中注册ofbiz实现的脚本引擎 getRequestHandler(); //初始化request han ...