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