await之后的线程问题
之前看了园子里的一篇文章「async & await的前世今生」,收益颇多。而其中有句话被博主特意用红色标注,所以留意多看了几眼,「await 之后不会开启新的线程(await 从来不会开启新的线程)」。在MSDN上找到的相关资料也佐证了其正确性——The async and await keywords don't cause additional threads to be created. Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active.(async 和 await 关键字不会导致创建其他线程。 因为异步方法不会在其自身线程上运行,因此它不需要多线程。 只有当方法处于活动状态时,该方法将在当前同步上下文中运行并使用线程上的时间。)
再建立一个Windows Forms应用工程,写点代码更形象地说明问题:
private void Form1_Load(object sender, EventArgs e)
{
PrintDataAsync();
Debug.Print("three");
} private async void PrintDataAsync()
{
Task<int> result = CalculateDataAsync();
Debug.Print("second");
int data = await result;
Debug.Print("last:" + data);
} private async Task<int> CalculateDataAsync()
{
Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
Debug.Print("first");
int result = 0;
for (int i = 0; i < 10; i++)
{
result += i;
}
await Task.Delay(1000);
Debug.Print("four");
Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
return result;
};
程序的结果如预期的一样,Output窗口中可以看到以下内容:
8 : False
first
second
three
four
8 : False
last:45
await之前的ManagedThreadId值与之后的ManagedThreadId值一致,IsThreadPoolThread始终是False,说明当前线程没有发生改变,也没有产生新的线程。
但如果建立的是Console应用工程,结果就不同了。
static void Main(string[] args)
{
PrintDataAsync();
Console.WriteLine("three");
Console.Read();
} private static async void PrintDataAsync()
{
Task<int> result = CalculateDataAsync();
Console.WriteLine("second");
int data = await result;
Console.WriteLine("last:" + data);
} private static async Task<int> CalculateDataAsync()
{
Console.WriteLine(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
Console.WriteLine("first");
int result = 0;
for (int i = 0; i < 10; i++)
{
result += i;
}
await Task.Delay(1000);
Console.WriteLine("four");
Console.WriteLine(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
return result;
}
这段代码的执行结果:
8 : False
first
second
three
four
10 : True
last:45
ManagedThreadId在await之后发生了变化,IsThreadPoolThread也变为了True,说明不是同一个线程。
为什么会这样?再看一下MSDN中描述——「The method runs on the current synchronization context and uses time on the thread only when the method is active」,这里涉及到SynchronizationContext对象的使用。
在Windows Forms工程代码中加入 Debug.Print(SynchronizationContext.Current.ToString()); 检测代码,其输出是System.Windows.Forms.WindowsFormsSynchronizationContext。
而如果在Console工程中加入类似的检测代码 Console.WriteLine(SynchronizationContext.Current.ToString()); 则会抛出空引用异常,因为SynchronizationContext.Current在Console工程中的值为null。
又从MSDN Magazine找到SynchronizationContext相关的文章,其中有介绍到:By convention, if a thread’s current SynchronizationContext is null, then it implicitly has a default SynchronizationContext.(根据惯例,如果一个线程的当前 SynchronizationContext 为 null,那么它隐式具有一个默认 SynchronizationContext。)The default SynchronizationContext is applied to ThreadPool threads unless the code is hosted by ASP.NET.(默认 SynchronizationContext 应用于 ThreadPool 线程,除非代码由 ASP.NET 承载。)
这里提到了APS.NET,所以再建个Web Forms应用工程用于验证:
protected void Page_Load(object sender, EventArgs e)
{
PrintDataAsync();
Debug.Print("three");
} private async void PrintDataAsync()
{
Debug.Print(SynchronizationContext.Current.ToString());
Task<int> result = CalculateDataAsync();
Debug.Print("second");
int data = await result;
Debug.Print("last:" + data);
} private async Task<int> CalculateDataAsync()
{
Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
Debug.Print("first");
int result = 0;
for (int i = 0; i < 10; i++)
{
result += i;
}
await Task.Delay(1000);
Debug.Print("four");
Debug.Print(string.Format("{0} : {1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread));
return result;
}
输出结果:
System.Web.AspNetSynchronizationContext
8 : True
first
second
three
four
9 : True
last:45
ManagedThreadId值发生改变,IsThreadPoolThread始终是True,SynchronizationContext.Current值为System.Web.AspNetSynchronizationContext。
由三次试验及相关资料可以得出结论,await之后的线程依据SynchronizationContext在不同环境中的不同定义而产生不同的结果。所以「await 之后不会开启新的线程(await 从来不会开启新的线程)」的肯定句式改成「await 之后会开启新的线程吗? Maybe」这样的句式更加合适些。
最后补充一点,若是把第一个Windows Forms工程的代码 await Task.Delay(); 改成 await Task.Delay().ConfigureAwait(false); 的话,则可以得到第二个Console工程同样的结果。
await之后的线程问题的更多相关文章
- C# 中await前后执行线程的问题
悬赏园豆:20 [已解决问题] 浏览: 1763次 解决于 2018-08-15 22:43 今天有点疑惑就写了个测试的代码,发现控制台和Winform中不一样 比如: 控制台: ...Main( ...
- 浅谈C#中的 async await 以及对线程相关知识的复习
C#5.0以后新增了一个语法糖,那就是异步方法async await,之前对线程,进程方面的知识有过较为深入的学习,大概知道这个概念,我的项目中实际用到C#异步编程的场景比较少,就算要用到一般也感觉T ...
- java 线程 Lock 锁使用Condition实现线程的等待(await)与通知(signal)
一.Condition 类 在前面我们学习与synchronized锁配合的线程等待(Object.wait)与线程通知(Object.notify),那么对于JDK1.5 的 java.util.c ...
- async与await线程分配研究
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别
C#语法——泛型的多种应用 本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...
- 四、线程同步之Lock和Condition
Lock同步锁 Lock 在jdk1.5 提供了Lock以便执行同步操作,和synchronized不同的是Lock提供了显示的方法获取锁和释放锁.Lock提供了以下几个方法,请求和释放锁: voi ...
- C#~异步编程再续~await与async引起的w3wp.exe崩溃
返回目录 最近怪事又开始发生了,IIS的应用程序池无做挂掉,都指向同一个矛头,async,threadPool,Task,还有一个System.NullReferenceException,所以这些都 ...
- async 和 await小结
三大返回值: 返回类型 - Task<TResult> 返回类型 - Task 返回类型 - void 当你添加 async 关键字后,需要返回一个将用于后续操作的对象,请使用 Task& ...
- Java线程间通信方式剖析——Java进阶(四)
原创文章,同步发自作者个人博客,转载请在文章开头处以超链接注明出处 http://www.jasongj.com/java/thread_communication/ CountDownLatch C ...
随机推荐
- KinectV1+Ubuntu 14.04安装教程
前言 个人理解错误的地方还请不吝赐教,转载请标明出处,内容如有改动更新,请看原博:http://www.cnblogs.com/hitcm/ 如有任何问题,feel free to ...
- 有1,2,3一直到n的无序数组,排序
题目:有1,2,3,..n 的无序整数数组,求排序算法.要求时间复杂度 O(n), 空间复杂度O(1). 分析:对于一般数组的排序显然 O(n) 是无法完成的. 既然题目这样要求,肯定原先的数组有一定 ...
- property
一.property用法 property(fget=None, fset=None, fdel=None, doc=None) -> property attribute fget is a ...
- Archlinux 上 Nginx + PHP + Mariadb + DiscuzX2.5 安装小记
因为不好找下载集成服务器工具,而且他们自己又打包了一份 php 之类的程序,本身系统就已经有 php 还有数据库什么的了再搞一份受不了,最后选择了手动配置…… 这是一个在 Archlinux 上手动从 ...
- 用Action的属性接受参数
版本, @Override是Java5的元数据,自动加上去的一个标志,告诉你说下面这个方法是从父类/接口 继承过来的,需要你重写一次,这样就可以方便你阅读,也不怕会忘记@Override是伪代码,表示 ...
- SQL脚本IN在EF中的应用
C#查询条件中存在in,为了避免拼脚本,参数化查询数据库,提高安全性,规避脚本注入.网上找了好多,最后发现 SqlParameter 是无法实现in的操作,所以只能变相来实现,结果还是不错的,性能上各 ...
- Android 图片添加水印图片或者文字
给图片添加水印的基本思路都是载入原图,添加文字或者载入水印图片,保存图片这三个部分 添加水印图片: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 ...
- G.Interference Signal---河南省第八届程序设计大赛(dp)
G.Interference Signal 时间限制: 2 Sec 内存限制: 128 MB提交: 47 解决: 18[提交][状态] 题目描述 Dr.Kong’s laboratory moni ...
- Javascript重要知识点梳理
Javascript重要知识点梳理 一.Javascript流程控制 js中常用的数据类型 var关键字的使用 if – else if – else switch while for 二.Javas ...
- shell编写mysql备份工具
如需转载,请经本人同意. 这是之前写的一个备份脚本,调用的备份工具是xtrabackup 编写思路是:每周一全备份,备份后提取lSN号,对备份文件进行压缩,其余时候在LSN的基础上进行增量备份,并对3 ...