新建一个.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. linux下查看内存的使用情况

    windows上有各种软件可以进行“一键加速”之类的操作,释放掉一些内存(虽然我暂时不知道是怎么办到的,有待后续学习).而任务管理器也可以很方便地查看各进程使用的内存情况,如下图: 同样地,linux ...

  2. IntelliJ IDEA 16创建Web项目

    首先要理解一个概念:在IntelliJ IDEA中“new Project”相当于eclipse中的工作空间(Workspace),而“new Module”相当于eclipse中的工程(Projec ...

  3. Scarpy+selenium 结合使用

    首先要先在spider对象实例化时,同时实例化一个浏览器对象 # -*- coding: utf-8 -*- import scrapy from selenium import webdriver ...

  4. web前端开发需要具备的技能

    web前端开发需要具备以下7种技能: 1.页面标记(HTML) HTML页面固定,标签不多,相对来说学起来比较容易.编写HTML代码需遵循HTML代码规范(http://www.cnblogs.com ...

  5. 如何解决织梦DedeCMS后台模块管理列表不显示

    在使用织梦Dedecms的过程中,我们会遇到模块管理列表无法显示的问题,造成织梦模块管理列表无法显示的原因,可能有很多种,现小编总结了遇到过的一种方法仅供参考. 方法步骤一: 由于/data/modu ...

  6. ArrayList的使用方法详解(转)

    1.什么是ArrayList    ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 动态的增加和减少元素 实现了ICollection和I ...

  7. Windows操作系统----锁住命令行窗口

    第一步: 新建一个.txt文档,输入如下内容: @echo off echo. setlocal :checkpassword set /p password=请输入密码: if "%pas ...

  8. 转:hive面试题

    有一张很大的表:TRLOG该表大概有2T左右TRLOG:CREATE TABLE TRLOG(PLATFORM string,USER_ID int,CLICK_TIME string,CLICK_U ...

  9. SQL Server ->> SQL Server 2016新特性之 -- Dynamic Data Masking

    Dynamic Data Masking是为了防止敏感数据暴露给未经授权的用户,以一种最小开销和维护成本的形式.Dynamic Data Masking用于表的字段,相当于盖住字段数据的一部分.比如一 ...

  10. .net 面向对象程序设计深入](1)UML

    1.UML简介 Unified Modeling Language (UML)又称统一建模语言或标准建模语言. 简单说就是以图形方式表现模型,根据不同模型进行分类,在UML 2.0中有13种图,以下是 ...