目录 C#异步编程 一、异步编程基础 二、异步方法的工作原理 三、代码示例 四、编译后的底层实现 五、总结
C#异步编程
一、异步编程基础
异步编程是啥玩意儿 就是让程序在干等着某些耗时操作(比如等网络响应、读写文件啥的)的时候,能把线程腾出来干别的活儿,这样程序就能更灵敏、更高效啦。 跟同步编程不一样,同步编程就是老老实实等着操作完成才继续往下走,线程就一直被占着,多浪费啊。 异步编程的好处 响应快 :比如在做UI界面的时候,用了异步编程,界面就不会卡啦,用户体验贼棒。省资源 :不用让线程一直干等着,资源利用率就上去了。能扛更多活儿 :面对一大堆并发操作的时候,异步编程能轻松搞定,扩展性杠杠滴。
二、异步方法的工作原理
异步方法咋被编译的 你写个async
修饰的方法,编译器就把它变成一个状态机啦。 状态机会根据await
表达式把方法拆成好多个状态,就跟玩拼图一样。 状态机是咋干活的 状态机就是编译器生成的一个类,它得记着异步方法执行到哪儿了。 核心就是MoveNext
方法,它就像导演一样,指挥着异步操作一步步往下走。 每碰到一个await
,就切换一下状态。 await
底层是咋实现的 await
就整出个等待器(awaiter),专门等着异步操作完成。要是操作还没完,await
就记下当前状态,等操作完了再继续往下走。
三、代码示例
用HttpClient
干异步网络请求 弄个HttpClient
对象,用来发HTTP请求。 用GetStringAsync
方法,就能异步拿到指定URL的网页内容啦。 把拿到的内容打印出来,瞧一瞧成果。
using System ;
using System. Net. Http ;
using System. Threading. Tasks ; namespace asyncawait原理1
{ class Program { static async Task Main ( string [ ] args) { using ( HttpClient httpClient = new HttpClient ( ) ) { string html = await httpClient. GetStringAsync ( "https://www.baidu.com" ) ; Console. WriteLine ( html) ; } } }
}
异步读写文件 用File.WriteAllTextAsync
方法,能把文本异步写到指定路径的文件里。 用File.ReadAllTextAsync
方法,就能把文件内容异步读出来。 把读到的内容打印出来,看看对不对。
using System ;
using System. IO ;
using System. Threading. Tasks ; namespace asyncawait原理1
{ class Program { static async Task Main ( string [ ] args) { string txt = "hello world" ; string filename = @"E:\temp\1.txt" ; await File. WriteAllTextAsync ( filename, txt) ; Console. WriteLine ( "写入成功" ) ; string s = await File. ReadAllTextAsync ( filename) ; Console. WriteLine ( "文件内容:" + s) ; } }
}
四、编译后的底层实现
用ILSpy反编译DLL文件 ILSpy就是个反编译工具,能把DLL文件变回C#代码,方便咱们研究。 把DLL文件加载到ILSpy里,就能看到编译后的代码啦。
[ CompilerGenerated ]
private sealed class < > c__DisplayClass0_0 : IAsyncStateMachine
{ public int < > 1__state; public AsyncTaskMethodBuilder < > t__builder; public string [ ] args; private string < > s__1; private string < > s__3; private string < > s__6; private HttpClient < httpClient> __4; private string < html> __5; private string < txt> __2; private string < filename> __7; private void MoveNext ( ) { int num = this . < > 1__state; try { TaskAwaiter< string > awaiter; TaskAwaiter awaiter2; switch ( num) { default : this . < httpClient> __4 = new HttpClient ( ) ; goto case 0 ; case 0 : try { awaiter = this . < httpClient> __4. GetStringAsync ( "https://www.baidu.com" ) . GetAwaiter ( ) ; if ( ! awaiter. IsCompleted) { num = this . < > 1__state = 0 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter, ref this ) ; return ; } } catch ( Exception exception) { this . < > 1__state = - 2 ; this . < > t__builder. SetException ( exception) ; return ; } this . < html> __5 = awaiter. GetResult ( ) ; Console. WriteLine ( this . < html> __5) ; this . < txt> __2 = "hello yz" ; this . < filename> __7 = @"E:\temp\1.txt" ; awaiter2 = File. WriteAllTextAsync ( this . < filename> __7, this . < txt> __2) . GetAwaiter ( ) ; if ( ! awaiter2. IsCompleted) { num = this . < > 1__state = 1 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter2, ref this ) ; return ; } break ; case 1 : awaiter2 = this . < > s__1; this . < > s__1 = null ; num = this . < > 1__state = - 1 ; break ; } awaiter2. GetResult ( ) ; Console. WriteLine ( "写入成功" ) ; this . < > s__3 = null ; awaiter = File. ReadAllTextAsync ( this . < filename> __7) . GetAwaiter ( ) ; if ( ! awaiter. IsCompleted) { num = this . < > 1__state = 2 ; this . < > t__builder. AwaitUnsafeOnCompleted ( ref awaiter, ref this ) ; return ; } this . < > s__6 = awaiter. GetResult ( ) ; Console. WriteLine ( "文件内容:" + this . < > s__6) ; this . < > s__6 = null ; this . < > t__builder. SetResult ( ) ; } catch ( Exception exception) { this . < > 1__state = - 2 ; this . < > t__builder. SetException ( exception) ; return ; } this . < > 1__state = - 1 ; } void IAsyncStateMachine. MoveNext ( ) { } [ DebuggerHidden ] private void SetStateMachine ( IAsyncStateMachine stateMachine) { this . < > t__builder. SetStateMachine ( stateMachine) ; } void IAsyncStateMachine. SetStateMachine ( IAsyncStateMachine stateMachine) { this . SetStateMachine ( stateMachine) ; }
}
看看编译后的状态机代码 分析状态机类的结构,看看都有啥变量、MoveNext
方法长啥样。 瞧瞧awaiter
咋用的,状态咋切换的。 理解MoveNext
方法是干啥的 MoveNext
就是状态机的发动机,它决定了异步方法咋执行。在这个方法里,会根据当前状态执行对应的代码,碰到await
就暂停,安排好后续咋继续。
五、总结
异步方法编译过程回顾 再唠唠async
方法咋被编译成状态机的,状态机又咋根据await
拆分方法、驱动异步操作的。 await
到底在干啥 说白了,await
根本不是真的“等待”,而是靠状态机和等待器来实现的异步协作。 强调一下异步编程的好处,比如响应快、省资源、能扛更多活儿,还有啥场景适合用它。