分享
 
 
 

[C#]剖析异步编程语法糖: async和await

王朝学院·作者佚名  2016-05-20
窄屏简体版  字體: |||超大  

[C#]剖析异步编程语法糖: async和await一、难以被接受的async

自从C#5.0,语法糖大家庭又加入了两位新成员: async和await。然而从我知道这两个家伙之后的很长一段时间,我甚至都没搞明白应该怎么使用它们,这种全新的异步编程模式对于习惯了传统模式的人来说实在是有些难以接受,不难想象有多少人仍然在使用手工回调委托的方式来进行异步编程。C#中的语法糖非常多,从自动属性到lock、using,感觉都很好理解很容易就接受了,为什么偏偏async和await就这么让人又爱又恨呢?我想,不是因为它不好用(相反,理解了它们之后是非常实用又易用的),而是因为它来得太迟了!传统的异步编程在各种语言各种平台前端后端差不多都是同一种模式,给异步请求传递一个回调函数,回调函数中再对响应进行处理,发起异步请求的地方对于返回值是一无所知的。我们早就习惯了这样的模式,即使这种模式十分蹩脚。而async和await则打破了请求发起与响应接收之间的壁垒,让整个处理的逻辑不再跳过来跳过去,成为了完全的线性流程!线性才是人脑最容易理解的模式!广告时间:[C#]async和await刨根问底这篇随笔把本文未解决的问题都搞定了,并且对async和await的总体面貌做了最终总结,对调查过程没有兴趣希望直接看结果的可以直接戳进去~

二、理解async,谁被异步了

如果对于java有一定认识,看到async的使用方法应该会觉得有些眼熟吧?

//Javasynchronized void sampleMethod() { }

// C#async void SampleMethod() { }

说到这里我想对MS表示万分的感谢,幸好MS的设计师采用的简写而不是全拼,不然在没有IDE的时候(比如写上面这两个示例的时候)我不知道得检查多少次有没有拼错同步或者异步的单词。。。Java中的synchronized关键字用于标识一个同步块,类似C#的lock,但是synchronized可以用于修饰整个方法块。而C#中async的作用就是正好相反的了,它是用于标识一个异步方法。同步块很好理解,多个线程不能同时进入这一区块,就是同步块。而异步块这个新东西就得重新理解一番了。先看看async到底被编译成了什么吧:

1 .method PRivate hidebysig 2 instance void SampleMethod () cil managed 3 { 4 .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 5 01 00 1f 54 65 73 74 2e 50 72 6f 67 72 61 6d 2b 6 3c 53 61 6d 70 6c 65 4d 65 74 68 6f 64 3e 64 5f 7 5f 30 00 00 8 ) 9 .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (10 01 00 00 0011 )12 // Method begins at RVA 0x20b013 // Code size 46 (0x2e)14 .maxstack 215 .locals init (16 [0] valuetype Test.Program/'<SampleMethod>d__0',17 [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder18 )19 20 IL_0000: ldloca.s 021 IL_0002: ldarg.022 IL_0003: stfld class Test.Program Test.Program/'<SampleMethod>d__0'::'<>4__this'23 IL_0008: ldloca.s 024 IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder::Create()25 IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.Program/'<SampleMethod>d__0'::'<>t__builder'26 IL_0014: ldloca.s 027 IL_0016: ldc.i4.m128 IL_0017: stfld int32 Test.Program/'<SampleMethod>d__0'::'<>1__state'29 IL_001c: ldloca.s 030 IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.Program/'<SampleMethod>d__0'::'<>t__builder'31 IL_0023: stloc.132 IL_0024: ldloca.s 133 IL_0026: ldloca.s 034 IL_0028: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder::Start<valuetype Test.Program/'<SampleMethod>d__0'>(!!0&)35 IL_002d: ret36 } // end of method Program::SampleMethod

不管你们吓没吓到,反正我第一次看到是吓了一大跳。。。之前的空方法SampleMethod被编译成了这么一大段玩意。另外还生成了一个名叫'<SampleMethod>d__0'的内部结构体,整个Program类的结构就像这样:其他的暂时不管,先尝试把上面这段IL还原为C#代码:

1 void SampleMethod() 2 { 3 '<SampleMethod>d__0' local0; 4 AsyncVoidMethodBuilder local1; 5 6 local0.'<>4_this' = this; 7 local0.'<>t__builder' = AsyncVoidMethodBuilder.Create(); 8 local0.'<>1_state' = -1; 9 10 local1 = local0.'<>t__builder';11 local1.Start(ref local0);12 }

跟进看Start方法:

1 // System.Runtime.CompilerServices.AsyncVoidMethodBuilder2 [__DynamicallyInvokable, DebuggerStepThrough]3 public void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine4 {5 this.m_coreState.Start<TStateMachine>(ref stateMachine);6 }

继续跟进:

1 // System.Runtime.CompilerServices.AsyncMethodBuilderCore 2 [DebuggerStepThrough, SecuritySafeCritical] 3 internal void Start<TStateMachine>(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine 4 { 5 if (stateMachine == null) 6 { 7 throw new ArgumentNullException("stateMachine"); 8 } 9 Thread currentThread = Thread.CurrentThread;10 ExecutionContextSwitcher executionContextSwitcher = default(ExecutionContextSwitcher);11 RuntimeHelpers.PrepareConstrainedRegions();12 try13 {14 ExecutionContext.EstablishCopyOnWriteScope(currentThread, false, ref executionContextSwitcher);15 stateMachine.MoveNext();16 }17 finally18 {19 executionContextSwitcher.Undo(currentThread);20 }21 }

注意到上面黄底色的stateMachine就是自动生成的内部结构体'<SampleMethod>d__0',再看看自动生成的MoveNext方法,IL就省了吧,直接上C#代码:

1 void MoveNext() 2 { 3 bool local0; 4 Exception local1; 5 6 try 7 { 8 local0 = true; 9 }10 catch (Exception e)11 {12 local1 = e;13 this.'<>1__state' = -2;14 this.'<>t__builder'.SetException(local1);15 return;16 }17 18 this.'<>1__state' = -2;19 this.'<>t__builder'.SetResult()20 }

因为示例是返回void的空方法,所以啥也看不出来,如果在方法里头稍微加一点东西,比如这样:

async void SampleMethod(){ Thread.Sleep(1000); Console.WriteLine("HERE");}

然后再看看SampleMethod的IL:

1 .method private hidebysig 2 instance void SampleMethod () cil managed 3 { 4 .custom instance void [mscorlib]System.Runtime.CompilerServices.AsyncStateMachineAttribute::.ctor(class [mscorlib]System.Type) = ( 5 01 00 1f 54 65 73 74 2e 50 72 6f 67 72 61 6d 2b 6 3c 53 61 6d 70 6c 65 4d 65 74 68 6f 64 3e 64 5f 7 5f 30 00 00 8 ) 9 .custom instance void [mscorlib]System.Diagnostics.DebuggerStepThroughAttribute::.ctor() = (10 01 00 00 0011 )12 // Method begins at RVA 0x20bc13 // Code size 46 (0x2e)14 .maxstack 215 .locals init (16 [0] valuetype Test.Program/'<SampleMethod>d__0',17 [1] valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder18 )19 20 IL_0000: ldloca.s 021 IL_0002: ldarg.022 IL_0003: stfld class Test.Program Test.Program/'<SampleMethod>d__0'::'<>4__this'23 IL_0008: ldloca.s 024 IL_000a: call valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder::Create()25 IL_000f: stfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.Program/'<SampleMethod>d__0'::'<>t__builder'26 IL_0014: ldloca.s 027 IL_0016: ldc.i4.m128 IL_0017: stfld int32 Test.Program/'<SampleMethod>d__0'::'<>1__state'29 IL_001c: ldloca.s 030 IL_001e: ldfld valuetype [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder Test.Program/'<SampleMethod>d__0'::'<>t__builder'31 IL_0023: stloc.132 IL_0024: ldloca.s 133 IL_0026: ldloca.s 034 IL_0028: call instance void [mscorlib]System.Runtime.CompilerServices.AsyncVoidMethodBuilder::Start<valuetype Test.Program/'<SampleMethod>d__0'>(!!0&)35 IL_002d: ret36 } // end of method Program::SampleMethod

看出来什么变化了吗?????看不出来就对了,因为啥都没变。那追加的代码跑哪去了?!在这呢:

1 void MoveNext() 2 { 3 bool local0; 4 Exception local1; 5 6 try 7 { 8 local0 = true; 9 Thread.Sleep(1000);10 Console.WriteLine("HERE");11 }12 catch (Exception e)13 {14 local1 = e;15 this.'<>1__state' = -2;16 this.'<>t__builder'.SetException(local1);17 return;18 }19 20 this.'<>1__state' = -2;21 this.'<>t__builder'.SetResult()22 }

至今为止都没看到异步在哪发生,因为事实上一直到现在确实都是同步过程。Main方法里这么写:

static void Main(string[] args){ new Program().SampleMethod(); Console.WriteLine("THERE"); Console.Read();}

运行结果是这样的:

HERETHERE

"THERE"被"HERE"阻塞了,并没有异步先行。虽然到此为止还没看到异步发生,但是我们可以得出一个结论:async不会导致异步到底怎么才能异步?还是得有多个线程才能异步嘛,是时候引入Task了:

async void SampleMethod(){ Task.Run(() => { T

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有