新建一个.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是什么状态的更多相关文章

  1. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿!

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  2. C# Task中的Func, Action, Async与Await的使用

    在说Asnc和Await之前,先说明一下Func和Action委托, Task任务的基础的用法 1. Func Func是一种委托,这是在3.5里面新增的,2.0里面我们使用委托是用Delegate, ...

  3. 浅析C#中的Thread ThreadPool Task和async/await

    .net 项目中不可避免地要与线程打交道,目的都是实现异步.并发.从最开始的new Thread()入门,到后来的Task.Run(),如今在使用async/await的时候却有很多疑问. 先来看一段 ...

  4. C#中 Thread,Task,Async/Await,IAsyncResult 的那些事儿![转载]

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  5. C#中 Thread,Task,Async/Await 异步编程

    什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调 ...

  6. 详解C#中 Thread,Task,Async/Await,IAsyncResult的那些事儿

    说起异步,Thread,Task,async/await,IAsyncResult 这些东西肯定是绕不开的,今天就来依次聊聊他们 1.线程(Thread) 多线程的意义在于一个应用程序中,有多个执行部 ...

  7. PySpark 的背后原理--在Driver端,通过Py4j实现在Python中调用Java的方法.pyspark.executor 端一个Executor上同时运行多少个Task,就会有多少个对应的pyspark.worker进程。

    PySpark 的背后原理 Spark主要是由Scala语言开发,为了方便和其他系统集成而不引入scala相关依赖,部分实现使用Java语言开发,例如External Shuffle Service等 ...

  8. 从Thread,ThreadPool,Task, 到async await 的基本使用方法解读

    记得很久以前的一个面试场景: 面试官:说说你对JavaScript闭包的理解吧? 我:嗯,平时都是前端工程师在写JS,我们一般只管写后端代码. 面试官:你是后端程序员啊,好吧,那问问你多线程编程的问题 ...

  9. C#多线程和异步(二)——Task和async/await详解

    一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...

随机推荐

  1. Html5本地存储+本地数据库+离线存储

    首先介绍什么叫存储: cache:通常把它叫做缓存,功能就是把从DB,或者磁盘拿出来的东西放在缓存里面,这样的话可以减少读取磁盘的IO. 磁盘文件:通常把一些图片或者一些视频都存放在磁盘上. 数据库: ...

  2. git 永久性设置密码

    git 设置不需要输入密码 https方式每次都要输入密码,按照如下设置即可输入一次就不用再手输入密码的困扰而且又享受https带来的极速 设置记住密码(默认15分钟): git config --g ...

  3. js内存空间详细图解-笔记

    原文参考http://mp.weixin.qq.com/s/NGqdjhoU3MR9LD0yH6tKIw 栈-先进后出堆-类比成书于书架(形象),只要知道Key就可以找到value 基础数据类型(Un ...

  4. ArcGIS 10.3 for Server部署策略

    注:以下部署策略整理自官方文档,主要是为方便以后查阅各种部署方案. 1.单机部署与反向代理 1.1.反向代理 反向代理可采用ArcGIS Web Adaptor或者第三方的反向代理服务器. 1.2.部 ...

  5. Git学习-Git时光机之版本回退(二)

    Git,是Linus花了两周时间用C写的一个分布式版本控制系统.牛人该怎么定义? 零.结论先行 倒叙总结一下: HEAD指向的版本就是当前版本,因此,Git允许我们在版本的历史之间穿梭,使用命令git ...

  6. 第三次Scrum

    1.小组成员 周 斌舒 溢许嘉荣唐 浩黄欣欣廖帅元刘洋江薛思汝 2.小组第三次冲刺完成情况 github仓库小组的第三次任务是完成体系结构环境图和系统原型图.在体系结构设计中,分为上级系统----把目 ...

  7. arm汇编学习(三)

    一.ndk编译android上运行的c程序 新建个hello目录,底下要有jni目录,下面就是Android.mk文件 1.Android.mk文件内容如下: LOCAL_PATH:= $(call ...

  8. sqlserver索引维护(重新组织生成索引)

    sqlserver索引的维护 1:查看索引碎片大于百分三十以上的索引 select object_id= object_id,indexid = index_id,partitionnum = par ...

  9. how to do error handing with WCF by using attributes to log your errors z

    There are multiple ways to do error handling in WCF as listed by Pedram Rezaei Blog. The default way ...

  10. Oracle 查看占用undo大的sql语句

    select s.sid,s.serial#,s.sql_id,v.usn,segment_name,r.status, v.rssize/1024/1024 mb     from dba_roll ...