.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详解
一.什么是异步 同步和异步主要用于修饰方法.当一个方法被调用时,调用者需要等待该方法执行完毕并返回才能继续执行,我们称这个方法是同步方法:当一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务 ...
随机推荐
- Java-----思想认识
1.1 Java封装性思想的初步理解 从项目需求的角度出发,建立各个模块化的类,各个模块暂时不需要具体的方法描述,只需要各个模块的理想到位. 以银行项目为例.初步设想需要三个模块:银行(Bank类). ...
- maven父子项目
maven搭建父子项目 1.先建立一个父项目,建立项目的时候,选择 Create a simple project 点击 next,填写以下信息 点击finish就可以了. 2.接下来要建立一个子项 ...
- CSS性能优化新属性:will-change
---恢复内容开始--- will-change属性通过告诉浏览器什么属性.什么元素将会发生变化,可以对这些操作进行可能性的优化,由此提高CSS动画的执行效率. 这个属性可以有4个值: auto: 实 ...
- [小北De编程手记] : Lesson 02 - Selenium For C# 之 核心对象
从这一篇开始,开始正式的介绍Selenium 以及相关的组件,本文的将讨论如下问题: Selenium基本的概念以及在企业化测试框架中的位置 Selenium核心对象(浏览器驱动) Web Drive ...
- Windows API-----top level window
原文地址: http://blog.163.com/cumt_xl/blog/static/19071504420136911838683/ Q: What is a top-level window ...
- gitattributes中的filter
.gitattributes文件就是一个简单的text文本文件,它的作用是gives attributes to pathnames. 该文件中的一些配置可以为某些特定目录或者文件来设置,这样Git就 ...
- MongoDB学习笔记——概述
概述 MongoDB是一个跨平台,面向文档的数据库.MongoDB创建的数据库可以实现高可用性,高性能,并且能够轻松拓展. MongodbDB的运行方式主要给予两个概念:集合(collection)和 ...
- 高逼格UILabel的闪烁动画效果
高逼格UILabel的闪烁动画效果 最终效果图如下: 源码: YXLabel.h 与 YXLabel.m // // YXLabel.h // // Created by YouXianMing o ...
- CI框架, 参数验证
/** * 统一API参数检验方法 * * 调用示例 check_param(array('money' => array('required', 'integer', 'greater_tha ...
- 【深入理解JAVA虚拟机】第5部分.高效并发.2.线程安全和锁优化
1 概述 对于这部分的主题“高效并发”来讲,首先需要保证并发的正确性,然后在此基础上实现高效. 2 线程安全 <Java Concurrency In Practice> 的作者Brian ...