本文告诉大家 await 的高级用法,包括底层原理。

昨天看到太子写了一段代码,我开始觉得他修改了编译器,要不然下面的代码怎么可以编译通过

await "林德熙逗比";

需要知道,基本可以添加 await 都是可以等待的类型,如 Task 。如果一个类需要可以被等待,那么这个类必须满足以下条件

  • 类里有一个 GetAwaiter 函数

  • GetAwaiter 有返回值,返回值需要继承 INotifyCompletion 并且有 bool IsCompleted { get; },GetResult(),void OnCompleted(Action continuation) 定义

参见:如何实现一个可以用 await 异步等待的 Awaiter - walterlv

但是上面的代码使用的是一个字符串,什么时候可以修改继承字符串?

先让我来说下 await 原理,因为知道了原理,上面的代码实现很简单。看完了本文,你就会知道如何让几乎所有类型包括 int 、string 、自定义的类都支持 await 。

如果真的不想看原理,那么请直接调到文章的最后,看到最后很快就知道如何做。

原理

在 .net 4.5 之后,框架默认提供 async 和 await 的语法糖,这时千万不要认为进入 await 就会进入一个新的线程,实际上不一定会进入一个新的线程才会调用 await 。

那么 await 的语法糖写的是什么?实际上就是以前的 Begin xx 和 End xx 的语法糖。

古时候的写法:

foo.Beginxx();foo.Endxx(传入委托);

这样大家就无法在一个流程写完,需要分为两个东西,而在 Continus with 下,就需要传入委托。如果委托里又使用了异步,那么又需要传入委托

       task.ContinueWith(_ =>{Task t1 = new Task(() => { });t1.ContinueWith((t2) =>{//可以看到如果进入很多的委托});});

所以这时就使用了 await ,可以让大家按照顺序写。

await task;
Task t1 = new Task(() => { });
await t1;
//可以看到这时不需要进入委托

实际上 await 是在编译时支持的,请看进阶篇:以IL为剑,直指async/await - 布鲁克石 - 博客园

而且千万不要认为 await 一定会进入一个新的线程,实际上他只是把需要写在多处的代码,可以按照流写下载,和写同步代码一样。如果感兴趣 await 不一定会进入一个新的线程请看 There Is No Thread

使用

因为 await 需要找到一个 GetAwaiter 函数,这个函数即使是扩展方法也可以,所以其实上面的代码是这样写的

public static class KvpbamjhKsvm{public static HeabdsdnbKevx GetAwaiter(this string str){return new HeabdsdnbKevx();}}public class HeabdsdnbKevx : INotifyCompletion{public bool IsCompleted { get; }public void GetResult(){}/// <inheritdoc />public void OnCompleted(Action continuation){}}

HeabdsdnbKevx 就是一个可以等待的类型

现在就可以写出下面的代码

        private static void Main(string[] args){DdngSiwchjux();}private static async void DdngSiwchjux(){await "林德熙逗比";}

当然,上面的这个代码可以运行,不过不会返回什么。下面让我加上一句代码。

        private static void Main(string[] args){DdngSiwchjux();}private static async void DdngSiwchjux(){await "林德熙逗比";Console.WriteLine("csdn");}

这时可以看到,Console.WriteLine("csdn");不会运行,因为这时如果在 OnCompleted 函数打断点就可以看到,执行await "林德熙逗比"之后就进入OnCompleted 函数。从上面的原理可以知道,这个函数传入的参数就是两个awaitawait和函数结束之间的代码。如果需要让Console.WriteLine("csdn");运行,那么只需要在OnCompleted运行参数

   public void OnCompleted(Action continuation){continuation();}

但是作为一个挖坑专业的大神,怎么可以就扩展 string ,下面我把 int 进行扩展

    public static class KvpbamjhKsvm{public static HeabdsdnbKevx GetAwaiter(this int dxpbnzSce){return new HeabdsdnbKevx();}}

随意写一个值,然后进行等待

现在我准备在 object 加一个扩展方法,所有类型都可以等待,然后把这个扩展方法的 namespace 写为 System ,这样大家就不知道这个是我写的,过了一年我就告诉大家这是 C# 的特性,所有的类都可以等待。但是这个特性需要开光才可以使用,你们直接建的项目没有开光所以没法使用这个特性。

等待和不等待的区别

虽然很多时候从原理上看,等待和不等待只是调用时机的问题。但是依旧遇到一些小伙伴一直以为全部的异步方法都需要await,看到我写了没有直接await的代码觉得很诡异,所以我在这里做个实验给大家看。

下面的代码是最常见的代码,在 async Task 的方法使用 await ,这样就会等待这个方法完成,代码就和同步代码一样。

 await GagarLerecel();
private static async Task GagarLerecel()

例如我这样写

            await GagarLerecel();private static async Task GagarLerecel(){Write("GagarLerecel 开始");await Task.Delay(100);Write("GagarLerecel 完成");}

输出就是按照顺序输出

GagarLerecel 开始
GagarLerecel 完成

如果我修改一下代码,创建一个新的函数 CoujafuDarso 里面的代码和上面函数相同

        private static async Task CoujafuDarso(){Write("CoujafuDarso开始");await Task.Delay(100);Write("CoujafuDarso结束");}

但是不在调用 CoujafuDarso 使用 await ,而是使用一个变量

            var aa = CoujafuDarso();Write("其他代码");await aa;

就是这样的代码,我的小伙伴说,这样写不清真,实际上这样写也是清真的代码。在调用 CoujafuDarso 会在代码到第一个 await 函数就返回,于是先执行了CoujafuDarso开始,然后函数返回,执行Write("其他代码"),在最后await aa才等待函数把所有代码执行完成。所以可以看到下面输出

CoujafuDarso开始
其他代码
CoujafuDarso结束

但是不加 await 的呢?也就是函数一直都没有等待,我再写一个函数BotujawWorpay

        private static async Task BotujawWorpay(){Write("BotujawWorpay开始");await Task.Delay(100);Write("BotujawWorpay结束");}

调用的时候没有等待

            BotujawWorpay();Write("CesearJemmeme");

这时会在输出CesearJemmeme之后,某个时间继续执行函数

BotujawWorpay开始
CesearJemmeme
BotujawWorpay结束

这样和使用 void 函数有什么区别?

在执行的函数遇到第一个 await 就会返回,这样就可以继续执行函数下面的代码

输出下面代码

德熙逗比代码
BarpooseewhowGelpousacall 代码1 线程1
德熙逗比状态开始
BarpooseewhowGelpousacall 代码2 线程5
BarpooseewhowGelpousacall 代码3 线程4
BarpooseewhowGelpousacall 完成 线程5

多线程

不是所有的 await 都会开多线程,如下面的代码

       static void Main(string[] args){Write("开始");Write("线程" + Thread.CurrentThread.ManagedThreadId);CeaXisci();Task.Run(async () =>{await Task.Delay(1000);MouvaypuNasjo();});while (true){Console.Read();}}private static async Task BarpooseewhowGelpousacall(){Write("BarpooseewhowGelpousacall 代码1 线程" + Thread.CurrentThread.ManagedThreadId);await Task.Delay(10);Write("BarpooseewhowGelpousacall 代码2 线程" + Thread.CurrentThread.ManagedThreadId);await Task.Delay(10);Write("BarpooseewhowGelpousacall 完成 线程" + Thread.CurrentThread.ManagedThreadId);}

也就是在没有Task.Delay分开的代码,只要使用了 await 那么就可以在同个线程运行,请看输出。在最后的BarpooseewhowGelpousacall 完成和这个函数后面的代码都在同一个线程运行,而上面的代码,可能是在同个线程,也可能在不同的线程

开始
线程1
CeaXisci 开始 线程1
BarpooseewhowGelpousacall 代码1 线程1
BarpooseewhowGelpousacall 代码2 线程5
BarpooseewhowGelpousacall 完成 线程4
CeaXisci 开始 完成4

相关博客

使用 Task.Wait()?立刻死锁(deadlock) - walterlv

如何实现一个可以用 await 异步等待的 Awaiter

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入

lterlv.github.io/post/deadlock-in-task-wait.html )

如何实现一个可以用 await 异步等待的 Awaiter

我搭建了自己的博客 https://blog.lindexi.com/ 欢迎大家访问,里面有很多新的博客。只有在我看到博客写成熟之后才会放在csdn或博客园,但是一旦发布了就不再更新

如果在博客看到有任何不懂的,欢迎交流,我搭建了 dotnet 职业技术学院 欢迎大家加入


本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接:http://blog.csdn.net/lindexi_gd ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

C# await 高级用法相关推荐

  1. foreach用法_25个你不得不知道的数组reduce高级用法

    作者:JowayYoung 仓库:Github.CodePen 博客:掘金.思否.知乎.简书.头条.CSDN 公众号:IQ前端 联系我:关注公众号后有我的微信哟 特别声明:原创不易,未经授权不得对此文 ...

  2. html表格标签高级应用,asp.net core标签助手的高级用法TagHelper+Form

    上一篇博客我讲解了TagHelper的基本用法和自定义标签的生成,那么我就趁热打铁,和大家分享一下TagHelper的高级用法~~,大家也可以在我的博客下随意留言. 对于初步接触asp.net cor ...

  3. Python多线程多进程、异步、异常处理等高级用法

    文章目录 前言 多线程多进程 多线程 多进程 协程 总结 异步 基本概念 异步编程 asyncio aiohttp 异常 常见异常 异常处理 自定义异常 lambda表达式 lambda表达式用法 高 ...

  4. ES6模块化与异步编程高级用法

    ES6模块化与异步编程高级用法 一.学习目标 能够知道如何使用ES6的模块化语法 能够知道如何使用Promise解决回调地域的问题 能够知道如何使用async/await 简化Promise的调用 能 ...

  5. JS数组reduce的25个高级用法

    reduce作为ES5新增的常规数组方法之一,对比forEach .filter和map,在实际使用上好像有些被忽略,下面这篇文章主要给大家介绍了关于JS数组reduce你不得不知道的25个高级用法, ...

  6. java reduce.mdn_编程语言你不得不知道的数组reduce高级用法

    背景 距离上一篇技术文章<1.5万字概括ES6全部特性>发布到现在,已经有整整4个月没有输出过一篇技术文章了.哈哈,不是不想写,而是实在太忙,这段时间每天不是上班就是加班,完全没有自己的时 ...

  7. 20个你不得不知道的数组reduce高级用法

    目录 1 背景 2 高级用法 3 兼容和性能 4 结语 1 背景 reduce 作为ES5新增的常规数组方法之一,对比 forEach .filter 和 map,在实际使用上好像有些被忽略,发现身边 ...

  8. es6模块化和异步编程高级用法

    es6模块化和异步编程高级用法 node.js 遵循了 CommonJS 的模块化规范.其中: ⚫ 导入其它模块使用 require() 方法 ⚫ 模块对外共享成员使用 module.exports ...

  9. vim的高级用法配置以及在系统中如何获取帮助

    vim的高级用法配置以及在系统中如何获取帮助 1 vim的三种模式 1.1 使用方法 1.2 vim模式 2 vim工作的基本配置 2.1 临时设定(set设定) 2.2 永久设定方式 3 搜索 4 ...

最新文章

  1. python创建mysql数据库_python 怎么创建create mysql的数据库
  2. 新闻网站的详情页面流程分析
  3. 不想学python-为什么自学python总是坚持不下去,这篇文章给你解答!
  4. python标准输入输出用来干什么_python 以标准输出(sys.stdout)为例,看python的标准输入、标准错误输出...
  5. 【剑指offer】面试题66:构建乘积数组(Java)
  6. android半透明闪退,(Android)react-native-splash-screen实践-解决react-native打包好后启动白屏的问题...
  7. 左程云 Java 笔记--排序
  8. linux清除密码记录
  9. 圆柱体积怎么算立方公式_圆柱的体积换算立方怎么算
  10. dedecms织梦 list列表页pagesize数量不对
  11. 做程序员10年有感,程序员必须要懂的---转自java诺曼底_kleen
  12. P背景软件测试,软件测试的背景和发展
  13. php html5定位,HTML HTML5 地理定位 - 闪电教程JSRUN
  14. iOS第三方支付(支付宝SDK)
  15. Android中文乱码的分析与解决
  16. 超宽带(UWB)无线通信技术介绍
  17. 微信小程序登陆凭证校验出现{errcode:40029,errmsg:invalid code, hints: [ req_id: weh8ka0297hc58 ]}
  18. 迅雷下载iso镜像失败(文件重命名失败)
  19. 使用Win32 SDK开发屏幕保护程序
  20. mybatis技术全攻略指南

热门文章

  1. 护肤-知乎推荐的护肤品
  2. 极客头条8月份活跃成员奖励名单(20150909)
  3. 关于微信PC版内置浏览器和IOS11以下系统打开页面白屏的问题
  4. iOS 采用@available(iOS 11.0, *)适配 iOS11,会引起调用系统相册时,系统界面上移问题
  5. 10月15日科技资讯|华为鸿蒙 2020 年将成第五大操作系统;贾跃亭债务净额约 20 亿美金;Jboot 2.2.3 发布 | 极客头条
  6. php视频吾爱破解,php 32行代码简单实现抖音无水印视频api,附前台页面
  7. Float,Double转16进制
  8. 【学术小白如何写好论文】引言
  9. 江苏大学 计算机调剂,江苏大学2019年考研调剂信息
  10. Android 11 截图流程梳理