NetCore高階系列文章04---async、await原理揭秘

愛生活,愛程式碼發表於2023-12-28

一、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背後的執行緒切換

三、非同步方法與多執行緒的關係

四、CancellationToken在非同步方法中的使用

 

相關文章