分享
 
 
 

探索c#之Async、Await剖析

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

探索c#之Async、Await剖析 2015-06-15 08:35 by 蘑菇先生, ... 阅读, ... 评论, 收藏, 编辑 阅读目录:基本介绍基本原理剖析内部实现剖析重点注意的地方总结基本介绍Async、Await是net4.x新增的异步编程方式,其目的是为了简化异步程序编写,和之前APM方式简单对比如下。

APM方式,BeginGetRequestStream需要传入回调函数,线程碰到BeginXXX时会以非阻塞形式继续执行下面逻辑,完成后回调先前传入的函数。

HttpWebRequest myReq =(HttpWebRequest)WebRequest.Create("http://cnblogs.com/"); myReq.BeginGetRequestStream(); //to do

Async方式,使用Async标记Async1为异步方法,用Await标记GetRequestStreamAsync表示方法内需要耗时的操作。主线程碰到await时会立即返回,继续以非阻塞形式执行主线程下面的逻辑。当await耗时操作完成时,继续执行Async1下面的逻辑

static async void Async1() { HttpWebRequest myReq = (HttpWebRequest)WebRequest.Create("http://cnblogs.com/"); await myReq.GetRequestStreamAsync(); //to do }

上面是net类库实现的异步,如果要实现自己方法异步。APM方式:

public delegate int MyDelegate(int x); MyDelegate mathDel = new MyDelegate((a) => { return 1; }); mathDel.BeginInvoke(1, (a) => { },null);

Async方式:

static async void Async2() { await Task.Run(() => { Thread.Sleep(500); Console.WriteLine("bbb"); }); Console.WriteLine("ccc"); } Async2(); Console.WriteLine("aaa");

对比下来发现,async/await是非常简洁优美的,需要写的代码量更少,更符合人们编写习惯。 因为人的思维对线性步骤比较好理解的。

APM异步回调的执行步骤是:A逻辑->假C回调逻辑->B逻辑->真C回调逻辑,这会在一定程度造成思维的混乱,当一个项目中出现大量的异步回调时,就会变的难以维护。Async、Await的加入让原先这种混乱的步骤,重新拨正了,执行步骤是:A逻辑->B逻辑->C逻辑。

基本原理剖析作为一个程序员的自我修养,刨根问底的好奇心是非常重要的。 Async刚出来时会让人有一头雾水的感觉,await怎么就直接返回了,微软怎么又出一套新的异步模型。那是因为习惯了之前的APM非线性方式导致的,现在重归线性步骤反而不好理解。 学习Async时候,可以利用已有的APM方式去理解,以下代码纯属虚构。比如把Async2方法想象APM方式的Async3方法:

static async void Async3() { var task= await Task.Run(() => { Thread.Sleep(500); Console.WriteLine("bbb"); }); //注册task完成后回调 task.RegisterCompletedCallBack(() => { Console.WriteLine("ccc"); }); }

上面看其来就比较好理解些的,再把Async3方法想象Async4方法:

static void Async4() { var thread = new Thread(() => { Thread.Sleep(500); Console.WriteLine("bbb"); }); //注册thread完成后回调 thread.RegisterCompletedCallBack(() => { Console.WriteLine("ccc"); }); thread.Start(); }

这样看起来就非常简单明了,连async都去掉了,变成之前熟悉的编程习惯。虽然代码纯属虚构,但基本思想是相通的,差别在于实现细节上面。

内部实现剖析作为一个程序员的自我修养,严谨更是不可少的态度。上面的基本思想虽然好理解了,但具体细节呢,编程是个来不得半点虚假的工作,那虚构的代码完全对不住看官们啊。

继续看Async2方法,反编译后的完整代码如下:

internal class PRogram{ // Methods [AsyncStateMachine(typeof(<Async2>d__2)), DebuggerStepThrough] private static void Async2() { <Async2>d__2 d__; d__.<>t__builder = AsyncVoidMethodBuilder.Create(); d__.<>1__state = -1; d__.<>t__builder.Start<<Async2>d__2>(ref d__); } private static void Main(string[] args) { Async2(); Console.WriteLine("aaa"); Console.ReadLine(); } // Nested Types [CompilerGenerated] private struct <Async2>d__2 : IAsyncStateMachine { // Fields public int <>1__state; public AsyncVoidMethodBuilder <>t__builder; private object <>t__stack; private TaskAwaiter <>u__$awaiter3; // Methods private void MoveNext() { try { TaskAwaiter awaiter; bool flag = true; switch (this.<>1__state) { case -3: goto Label_00C5; case 0: break; default: if (Program.CS$<>9__CachedAnonymousMethodDelegate1 == null) { Program.CS$<>9__CachedAnonymousMethodDelegate1 = new Action(Program.<Async2>b__0); } awaiter = Task.Run(Program.CS$<>9__CachedAnonymousMethodDelegate1).GetAwaiter(); if (awaiter.IsCompleted) { goto Label_0090; } this.<>1__state = 0; this.<>u__$awaiter3 = awaiter; this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Async2>d__2>(ref awaiter, ref this); flag = false; return; } awaiter = this.<>u__$awaiter3; this.<>u__$awaiter3 = new TaskAwaiter(); this.<>1__state = -1; Label_0090: awaiter.GetResult(); awaiter = new TaskAwaiter(); Console.WriteLine("ccc"); } catch (Exception exception) { this.<>1__state = -2; this.<>t__builder.SetException(exception); return; } Label_00C5: this.<>1__state = -2; this.<>t__builder.SetResult(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine param0) { this.<>t__builder.SetStateMachine(param0); } } public delegate int MyDelegate(int x);} Collapse Methods

View Code发现async、await不见了,原来又是编译器级别提供的语法糖优化,所以说async不算是全新的异步模型。 可以理解为async更多的是线性执行步骤的一种回归,专门用来简化异步代码编写。从反编译后的代码看出编译器新生成一个继承IAsyncStateMachine的状态机结构asyncd(代码中叫<Async2>d__2,后面简写AsyncD),下面是基于反编译后的代码来分析的。

IAsyncStateMachine最基本的状态机接口定义:

public interface IAsyncStateMachine{ void MoveNext(); void SetStateMachine(IAsyncStateMachine stateMachine);}

既然没有了async、await语法糖的阻碍,就可以把代码执行流程按线性顺序来理解,其整个执行步骤如下:

1. 主线程调用Async2()方法2. Async2()方法内初始化状态机状态为-1,启动AsyncD3. MoveNext方法内部开始执行,其task.run函数是把任务扔到线程池里,返回个可等待的任务句柄。MoveNext源码剖析:

//要执行任务的委托

Program.CS$<>9__CachedAnonymousMethodDelegate1 = new Action(Program.<Async2>b__0);

//开始使用task做异步,是net4.0基于任务task的编程方式。

awaiter =Task.Run(Program.CS$<>9__CachedAnonymousMethodDelegate1).GetAwaiter();

//设置状态为0,以便再次MoveNext直接break,执行switch后面的逻辑,典型的状态机模式。

this.<>1__state = 0;

//返回调用async2方法的线程,让其继续执行主线程后面的逻辑

this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, Program.<Async2>d__2>(ref awaiter, ref this);return;

4. 这时就已经有2个线程在跑了,分别是主线程和Task.Run在跑的任务线程。

5. 执行主线程后面逻辑输出aaa,任务线程运行完成后输出bbb、在继续执行任务线程后面的业务逻辑输出ccc。

Label_0090: awaiter.GetResult(); awaiter = new TaskAwaiter();Console.WriteLine("ccc");

这里可以理解为async把整个主线程同步逻辑,分拆成二块。 第一块是在主线程直接执行,第二块是在任务线程完成后执行, 二块中间是任务线程在跑,其源码中awaiter.GetResult()就是在等待任务线程完成后去执行第二块。 从使用者角度来看执行步骤即为: 主线程A逻辑->异步任务线程B逻辑->主线程C逻辑。

Test(); Console.WriteLine("A逻辑"); static async void Test() { await Task.Run(() => { Thread.Sleep(1000); Console.WriteLine("B逻辑"); }); Console.WriteLine("C逻辑"); }

回过头来对比下基本原理剖析小节中的虚构方法Async4(),发现区别在于一个是完成后回调,一个是等待完成后再执行,这也是实现异步最基本的两大类方式。

重点注意的地方主线程A逻辑->异步任务线程B逻辑->主线程C逻辑。注意:这3个步骤是有可能会使用同一个线程的,也可能会使用2个,甚至3个线程。可以用Thread.CurrentThread.ManagedThreadId测试下得知。

Async7(); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); static async void Async7() { await Task.Run(() => { Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }); Console.WriteLine(Thread.CurrentThread.ManagedThreadId); }

正由于此,才会有言论说Async不用开线程,也有说需要开线程的,从单一方面来讲都是对的,也都是错的。 上面源码是从

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有