NetCore高級系列文章04---async、await原理揭祕

一、async、await本質

直接說結論:它們是C#提供的語法糖,編譯器編譯後是狀態機的調用。

先看如下的一段代碼,要main方法中調用了三個await方法

 將此dll進行反編譯爲4.0的代碼如下:

 可見到兩個Main方法,也就是說我們在程序中Main方法上加了async關鍵詞,編譯器會編譯成一個是異步的一個是非異步方法,程序還是將非異步的方法作爲入口函數。進入函數

在該函數中調了異步的Main方法,再進入:

 在該函數中創建了一個狀態機,將參數傳給狀態機,並調用期Start方法,可知異步方法實際上是狀態機方法的調用

進入狀態機類型<Main>d__0

private sealed class <Main>d__0 : IAsyncStateMachine
{
    public int <>1__state;

    public AsyncTaskMethodBuilder <>t__builder;

    public string[] args;

    private string <text>5__1;

    private HttpClient <httpClient>5__2;

    private string <html>5__3;

    private string <>s__4;

    private string <>s__5;

    private TaskAwaiter<string> <>u__1;

    private TaskAwaiter <>u__2;

    private void MoveNext()
    {
        int num = <>1__state;
        try
        {
            TaskAwaiter awaiter2;
            TaskAwaiter<string> awaiter;
            switch (num)
            {
            default:
                <httpClient>5__2 = new HttpClient();
                goto case 0;
            case 0:
                try
                {
                    TaskAwaiter<string> awaiter3;
                    if (num != 0)
                    {
                        awaiter3 = <httpClient>5__2.GetStringAsync("https://www.baidu.com").GetAwaiter();
                        if (!awaiter3.IsCompleted)
                        {
                            num = (<>1__state = 0);
                            <>u__1 = awaiter3;
                            <Main>d__0 stateMachine = this;
                            <>t__builder.AwaitUnsafeOnCompleted(ref awaiter3, ref stateMachine);
                            return;
                        }
                    }
                    else
                    {
                        awaiter3 = <>u__1;
                        <>u__1 = default(TaskAwaiter<string>);
                        num = (<>1__state = -1);
                    }
                    <>s__4 = awaiter3.GetResult();
                    <html>5__3 = <>s__4;
                    <>s__4 = null;
                    Console.WriteLine(<html>5__3);
                    <html>5__3 = null;
                }
                finally
                {
                    if (num < 0 && <httpClient>5__2 != null)
                    {
                        ((IDisposable)<httpClient>5__2).Dispose();
                    }
                }
                <httpClient>5__2 = null;
                awaiter2 = File.WriteAllTextAsync("E:\\test.txt", "zhengwei").GetAwaiter();
                if (!awaiter2.IsCompleted)
                {
                    num = (<>1__state = 1);
                    <>u__2 = awaiter2;
                    <Main>d__0 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter2, ref stateMachine);
                    return;
                }
                goto IL_015f;
            case 1:
                awaiter2 = <>u__2;
                <>u__2 = default(TaskAwaiter);
                num = (<>1__state = -1);
                goto IL_015f;
            case 2:
                {
                    awaiter = <>u__1;
                    <>u__1 = default(TaskAwaiter<string>);
                    num = (<>1__state = -1);
                    break;
                }
                IL_015f:
                awaiter2.GetResult();
                Console.WriteLine("寫入成功");
                awaiter = File.ReadAllTextAsync("E:\\test.txt").GetAwaiter();
                if (!awaiter.IsCompleted)
                {
                    num = (<>1__state = 2);
                    <>u__1 = awaiter;
                    <Main>d__0 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                    return;
                }
                break;
            }
            <>s__5 = awaiter.GetResult();
            <text>5__1 = <>s__5;
            <>s__5 = null;
            Console.WriteLine("文件內容" + <text>5__1);
        }
        catch (Exception exception)
        {
            <>1__state = -2;
            <text>5__1 = null;
            <>t__builder.SetException(exception);
            return;
        }
        <>1__state = -2;
        <text>5__1 = null;
        <>t__builder.SetResult();
    }

    void IAsyncStateMachine.MoveNext()
    {
        //ILSpy generated this explicit interface implementation from .override directive in MoveNext
        this.MoveNext();
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine stateMachine)
    {
    }

    void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine)
    {
        //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine
        this.SetStateMachine(stateMachine);
    }
}

在此狀態機類中可以看到我們自己寫的Main方法中的主要代碼已編譯到了方法MoveNext()中,並將我們的局部變量編譯成了成員變量

方法中有一個case,我們自己寫的三個異步方法被編譯後拆到了多個case中去了,MoveNext方法會根據不同的num被多次調用

編譯的代碼中可見到在每次調用異步方法時都會去判斷是否完成

 

 

 

編譯後的代碼意思是當執行到string html = await httpClient.GetStringAsync("https://www.baidu.com");時,如果沒有完成就直接返回了,並不會再往下執行

只有當此段代碼執行完成後會繼續往下執行,當執行到第二個異步方法await File.WriteAllTextAsync(@"E:\test.txt","zhengwei")時又會返回,等待執行完後再往下執行

同理,執行第三步異步方法var text = await File.ReadAllTextAsync(@"E:\test.txt");也是如此

所有異步方法都 執行完成後,會break;跳出狀態機。

未完待續。。。

二、async背後的線程切換

await調用的等待期間,.net會把當前的線程返回線線程池,等異步方法調用執行完畢後,框架會從線程池再取出來一個線程執行後續的代碼。

代碼驗證

 運行的結果可知在這個方法中使用的並不是同一個進程,一個是1,一個是9

當然,有時寫入的內容少,也有可能方法開始和結束的線程id是一樣的, 

三、異步方法與多線程的關係

異步方法的代碼並不會自動在新的線程中執行,除非把代碼放到新線程中執行。

在方法調用前和方法調用中用的是同一個線程,都是1,並沒有開啓一個新的線程

 

 

除非把這個異步方法放到一個新的線程中:可以看到方法調用中的線程爲4,調用前的線程爲1。

 

 

 

四、CancellationToken在異步方法中的使用

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章