C#中async await異步關(guān)鍵字用法和異步的底層原理全解析
C#異步編程
一、異步編程基礎(chǔ)
異步編程是啥玩意兒
- 就是讓程序在干等著某些耗時(shí)操作(比如等網(wǎng)絡(luò)響應(yīng)、讀寫文件啥的)的時(shí)候,能把線程騰出來干別的活兒,這樣程序就能更靈敏、更高效啦。
- 跟同步編程不一樣,同步編程就是老老實(shí)實(shí)等著操作完成才繼續(xù)往下走,線程就一直被占著,多浪費(fèi)啊。
異步編程的好處
- 響應(yīng)快:比如在做UI界面的時(shí)候,用了異步編程,界面就不會(huì)卡啦,用戶體驗(yàn)賊棒。
- 省資源:不用讓線程一直干等著,資源利用率就上去了。
- 能扛更多活兒:面對(duì)一大堆并發(fā)操作的時(shí)候,異步編程能輕松搞定,擴(kuò)展性杠杠滴。
二、異步方法的工作原理
異步方法咋被編譯的
- 你寫個(gè)
async修飾的方法,編譯器就把它變成一個(gè)狀態(tài)機(jī)啦。 - 狀態(tài)機(jī)會(huì)根據(jù)
await表達(dá)式把方法拆成好多個(gè)狀態(tài),就跟玩拼圖一樣。
狀態(tài)機(jī)是咋干活的
- 狀態(tài)機(jī)就是編譯器生成的一個(gè)類,它得記著異步方法執(zhí)行到哪兒了。
- 核心就是
MoveNext方法,它就像導(dǎo)演一樣,指揮著異步操作一步步往下走。 - 每碰到一個(gè)
await,就切換一下狀態(tài)。
await底層是咋實(shí)現(xiàn)的
await就整出個(gè)等待器(awaiter),專門等著異步操作完成。- 要是操作還沒完,
await就記下當(dāng)前狀態(tài),等操作完了再繼續(xù)往下走。
三、代碼示例
用HttpClient干異步網(wǎng)絡(luò)請(qǐng)求
- 弄個(gè)
HttpClient對(duì)象,用來發(fā)HTTP請(qǐng)求。 - 用
GetStringAsync方法,就能異步拿到指定URL的網(wǎng)頁內(nèi)容啦。 - 把拿到的內(nèi)容打印出來,瞧一瞧成果。
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方法,就能把文件內(nèi)容異步讀出來。 - 把讀到的內(nèi)容打印出來,看看對(duì)不對(duì)。
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("文件內(nèi)容:" + s);
}
}
}四、編譯后的底層實(shí)現(xiàn)
用ILSpy反編譯DLL文件
- ILSpy就是個(gè)反編譯工具,能把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("文件內(nèi)容:" + 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()
{
// This method is implemented by the compiler-generated code.
}
[DebuggerHidden]
private void SetStateMachine(IAsyncStateMachine stateMachine)
{
this.<>t__builder.SetStateMachine(stateMachine);
}
void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
{
this.SetStateMachine(stateMachine);
}
}看看編譯后的狀態(tài)機(jī)代碼
- 分析狀態(tài)機(jī)類的結(jié)構(gòu),看看都有啥變量、
MoveNext方法長啥樣。 - 瞧瞧
awaiter咋用的,狀態(tài)咋切換的。
理解MoveNext方法是干啥的
MoveNext就是狀態(tài)機(jī)的發(fā)動(dòng)機(jī),它決定了異步方法咋執(zhí)行。- 在這個(gè)方法里,會(huì)根據(jù)當(dāng)前狀態(tài)執(zhí)行對(duì)應(yīng)的代碼,碰到
await就暫停,安排好后續(xù)咋繼續(xù)。
五、總結(jié)
異步方法編譯過程回顧
- 再嘮嘮
async方法咋被編譯成狀態(tài)機(jī)的,狀態(tài)機(jī)又咋根據(jù)await拆分方法、驅(qū)動(dòng)異步操作的。
await到底在干啥
- 說白了,
await根本不是真的“等待”,而是靠狀態(tài)機(jī)和等待器來實(shí)現(xiàn)的異步協(xié)作。 - 強(qiáng)調(diào)一下異步編程的好處,比如響應(yīng)快、省資源、能扛更多活兒,還有啥場(chǎng)景適合用它。
到此這篇關(guān)于C#中async await異步關(guān)鍵字用法和異步的底層原理的文章就介紹到這了,更多相關(guān)C# async await異步關(guān)鍵字內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#連接操作 MySQL 數(shù)據(jù)庫實(shí)例(使用官方驅(qū)動(dòng))
這篇文章主要介紹了C#連接操作 MySQL 數(shù)據(jù)庫實(shí)例(使用官方驅(qū)動(dòng)),本文講解了C#中的Mysql連接方法和SQL操作方法,需要的朋友可以參考下2015-02-02
Winform實(shí)現(xiàn)調(diào)用asp.net數(shù)據(jù)接口實(shí)例
這篇文章主要介紹了Winform實(shí)現(xiàn)調(diào)用asp.net數(shù)據(jù)接口的方法,以實(shí)例的形式講述了數(shù)據(jù)接口及反射辨別響應(yīng)的實(shí)現(xiàn)方法,具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2014-10-10

