目录
- 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
根本不是真的“等待”,而是靠状态机和等待器来实现的异步协作。 - 强调一下异步编程的好处,比如响应快、省资源、能扛更多活儿,还有啥场景适合用它。