C#语法——await与async的正确打开方式
C#5.0推出了新语法,await与async,但相信大家还是很少使用它们。关于await与async有很多文章讲解,但有没有这样一种感觉,你看完后,总感觉这东西很不错,但用的时候,总是想不起来,或者不知道该怎么用。
为什么呢?我觉得大家的await与async的打开方式不正确。
正确的打开方式
1、await 只能在标记了async的函数内使用。
2、await 等待的函数必须标记async。
有没有感觉这是个循环?没错,这就是个循环。这也就是为什么大家不怎么用他们的原因。这个循环很讨厌,那么怎么破除这个循环呢?
【很简单,await等待的是线程,不是函数。】
不理解吗?没关系,接着看下去。
下面从头来讲解,首先看这么一组对比
public static int NoAsyncTest()
{
return 1;
}
public static async Task<int> AsyncTest()
{
return 1;
}
async Task<int>等于int
这意味着我们在正常调用这两个函数时,他们是等效的。那么用async Task<int>来修饰int目的是什么呢?
目的是为了让这个方法这样被调用 await AsyncTest(),但直接这样调用,并不会开启线程,那这样费劲的修饰是不是就没什么意义了呢。
当然不是,那什么时候会让 await AsyncTest()有意义呢?
我们接着往下看,修改AsyncTest如下。然后,此时再调用await AsyncTest(),你会神奇的发现,依然没有卵用。。。
Excute方法正常执行,而AsyncTest内运行的线程,自己执行自己的。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await AsyncTest();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
} public static async Task<int> AsyncTest()
{
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
});
return 1;
}
别着急,我们稍作调整,在线程后面增加.GetAwaiter().GetResult()。这句话是干什么用的呢?是用来获取线程返回值的。
这个逻辑是这样的,如果想要获取线程返回结果,就自然要等待线程结束。
运行一下,我们将看下面的结果。
public static async Task<int> AsyncTest()
{
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
return 1;
}
但是,好像await AsyncTest();还是没启作用。没错,事实就是,他真的不会起作用。。。
那么怎么才能让他起作用呢?
首先,我们定义一个普通函数,他的返回值是一个Task,然后我们得到Task后,运行它,再用await等待这个Task。
于是我们就得到这样的结果。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
var waitTask = AsyncTestRun();
waitTask.Start();
int i = await waitTask;
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " i " + i);
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static Task<int> AsyncTestRun()
{
Task<int> t = new Task<int>(() => {
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
return 100;
});
return t;
}
如图,这样写await AsyncTest();就起作用了。
所以,还是那句话,await等待的是线程,不是函数。
但在图里,我们发现很奇怪的一点,结束Excute也是线程3,而不是线程1。也就是说,Await会对线程进行优化。
下面看下两组代码的对比,让我们就更清楚的了解下Await。
第一组,使用await等待线程。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await SingleAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
} public static async Task SingleAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest开始 " + DateTime.Now);
await Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
});
await Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
});
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " AwaitTest结束 " + DateTime.Now);
return;
}
第二组,使用等待线程结果,等待线程。
public static async void Excute()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 开始 Excute " + DateTime.Now);
await SingleNoAwait();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " 结束 Excute " + DateTime.Now);
}
public static async Task SingleNoAwait()
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait开始 " + DateTime.Now);
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run1 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
Task.Run(() =>
{
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " Run2 " + DateTime.Now);
Thread.Sleep(1000);
}).GetAwaiter().GetResult();
Console.WriteLine(Thread.CurrentThread.GetHashCode() + " SingleNoAwait结束 " + DateTime.Now);
return;
}
可以明确的看到,第二组,线程重新回到了主线程1中,而第一组,已经被优化到了线程4中。
await是一种很便捷的语法,他的确会让代码简洁一些,但他主动优化线程的功能,如果不了解就使用,可能会导致一些奇怪的BUG发生。
这也是官方为什么只提供了await调用服务的例子,因为,在程序内调用,await还是要了解后,再使用,才安全。
----------------------------------------------------------------------------------------------------
注:此文章为原创,任何形式的转载都请联系作者获得授权并注明出处!
若您觉得这篇文章还不错,请点击下方的【推荐】,非常感谢!
C#语法——await与async的正确打开方式的更多相关文章
- C#语法——泛型的多种应用 C#语法——await与async的正确打开方式 C#线程安全使用(五) C#语法——元组类型 好好耕耘 redis和memcached的区别
C#语法——泛型的多种应用 本篇文章主要介绍泛型的应用. 泛型是.NET Framework 2.0 版类库就已经提供的语法,主要用于提高代码的可重用性.类型安全性和效率. 泛型的定义 下面定义了 ...
- Console控制台的正确打开方式
Console控制台的正确打开方式 console对象提供了访问浏览器调试模式的信息到控制台 -- Console对象 |-- assert() 如果第一个参数断言为false,则在控制台输出错误信息 ...
- iOS开发小技巧--相机相册的正确打开方式
iOS相机相册的正确打开方式- UIImagePickerController 通过指定sourceType来实现打开相册还是相机 UIImagePickerControllerSourceTypeP ...
- Xcode 的正确打开方式——Debugging(转载)
Xcode 的正确打开方式——Debugging 程序员日常开发中有大量时间都会花费在 debug 上,从事 iOS 开发不可避免地需要使用 Xcode.这篇博客就主要介绍了 Xcode 中几种能 ...
- InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式
InnoDB缓冲池预加载在MySQL 5.7中的正确打开方式 https://mp.weixin.qq.com/s/HGa_90XvC22anabiBF8AbQ 在这篇文章里,我将讨论在MySQL 5 ...
- 任务队列和异步接口的正确打开方式(.NET Core版本)
任务队列和异步接口的正确打开方式 什么是异步接口? Asynchronous Operations Certain types of operations might require processi ...
- (一)Redis for Windows正确打开方式
目录 (一)Redis for Windows正确打开方式 (二)Redis for 阿里云公网连接 (三)Redis for StackExchange.Redis 下载地址 官网.中文网1 及 中 ...
- List的remove()方法的三种正确打开方式
转: java编程:List的remove()方法的三种正确打开方式! 2018年08月12日 16:26:13 Aries9986 阅读数 2728更多 分类专栏: leetcode刷题 版权声 ...
- C++11随机数的正确打开方式
C++11随机数的正确打开方式 在C++11之前,现有的随机数函数都存在一个问题:在利用循环多次获取随机数时,如果程序运行过快或者使用了多线程等方法,srand((unsigned)time(null ...
随机推荐
- 显著性检测(saliency detection)评价指标之NSS的Matlab代码实现
calcNSSscore.m function [ score ] = calcNSSscore( salMap, eyeMap ) %calcNSSscore Calculate NSS score ...
- Java基础-方法重载和方法重写的区别
什么是java方法重载 (1) 方法重载是让类以统一的方式处理不同类型数据的一种手段.多个同名函数同时存在,具有不同的参数个数/类型. 重载Overloading是一个类中多态性的一种表现. (2) ...
- Python创建微信机器人
微信,一个日活10亿的超级app,不仅在国内社交独领风骚,在国外社交也同样占有一席之地,今天我们要将便是如何用Python来生成一个微信机器人,突然想起鲁迅先生曾经说过的一句话:因为是微信机器人系列的 ...
- Spring Boot 2.0 WebFlux 教程 (一) | 入门篇
目录 一.什么是 Spring WebFlux 二.WebFlux 的优势&提升性能? 三.WebFlux 应用场景 四.选 WebFlux 还是 Spring MVC? 五.异同点 六.简单 ...
- C#读写EXCEL单元格的问题
最近, 我在用C#开发一个EXCEL Add-In的时候,发现了一些害人不浅的坑,特来总结列举如下: 这里我读写EXCEL引用的是using Excel = Microsoft.Office.Inte ...
- C#动态调用泛型类、泛型方法
在制作一个批量序列化工具时遇到了如下问题,在此记录一下,仅供参考. 主程序加载另一个程序集,将其中的所有类取出,然后对这些类分别调用泛型类或泛型方法.控制台程序解决方案如下: Main工程:提供Wor ...
- 前端基于Canvas生成等值面的方案
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在之前的项目中,我们做过基于PM2.5的站点监测数据对全区域进 ...
- 好代码是管出来的——使用Jenkins搭建CI服务器
Jenkins是一个开源的跨平台的CI工具,它可以部署在Windows.Linux等平台上,并且Jenkins提供了非常丰富的插件来帮助完成编译.测试.部署等工作. 本文将介绍在Windows平台上使 ...
- 远程连接身份验证错误,又找不到加密Oracle修正
远程连接服务器出现了错误,错误信息为:远程连接身份验证错误,又找不到加密Oracle修正. 服务器系统:Windows Server2016 客户端系统:Windows10家庭版和专业版 出错原因 ...
- 【机器学习笔记五】聚类 - k均值聚类
参考资料: [1]Spark Mlib 机器学习实践 [2]机器学习 [3]深入浅出K-means算法 http://www.csdn.net/article/2012-07-03/2807073- ...