第十七節:從狀態機的角度async和await的實現原理(新)


一. 深度剖析

准備:

  先給VS安裝一個插件ILSpy,這樣更容易反編譯代碼進行查看,另外要注意反編譯async和await的時候,要把C#代碼版本改為4.0哦。

1.什么是狀態機

 (1).含義:通常我們所說的狀態機(State Machine)指的是有限狀態自動機的簡稱,是現實事物運行規則抽象而成的一個數學模型,可以理解成一個狀態轉換圖。(狀態機是計算機科學的重要基礎概念之一,也可以說是一種總結歸納問題的思想,應用范圍非常廣泛)

 (2).例子:自動門有兩個狀態,open 和 closed ,closed 狀態下,如果讀取開門信號,那么狀態就會切換為 open 。open 狀態下如果讀取關門信號,狀態就會切換為 closed .

 (3).涉及到4個相關概念:

  A.狀態(State):一個狀態機至少包括兩個狀態.(例如上面自動門的例子,有 open 和 closed 兩個狀態。)

  B.事件(Event):事件就是執行某個操作的觸發條件或者口令.(對於自動門,“按下開門按鈕”就是一個事件。)

  C.動作(Action):事件發生以后要執行的動作,一個action對應一個函數.(事件是“按開門按鈕”,動作是“開門”)

  D.變換(Transition):從一個狀態轉換成另外一個狀態.(“開門過程”就是一個變換。)

 (4). C#的狀態機提供了IAsyncStateMachine接口,里面有MoveNext 和 SetStateMachine方法處理相應業務.

2. 狀態機分析

  async關鍵字標記方法是一個異步方法,編譯器通過這個標記去改造這個方法體為創建狀態機的方法。await是關鍵字是為了實現狀態機中的一個狀態, 每當有一個await,就會生成一個對應的狀態。狀態機就是根據這個狀態,去一步步的調用異步委托,然后回調,包括狀態機的解析。

(1).狀態機的默認狀態都是-1, 結束狀態都是-2.

(2).每await一次就會產生一個 TaskAwaiter<int> awaiter; 改變狀態機的狀態, 當有多個await的時候,每個await都會改變狀態機的狀態,比如 改為 0,1,2,3,4 等等, 分別表示 代碼中await xxx 這句話執行完成。

(3).狀態機的執行套路:

  A. 首先創建一個 <xxx>d_num 的方法, xxx代表方法名,num可能是0,1,2,3等,實現IAsyncStateMachine接口。

  B. 在MoveNext方法中, 源代碼中每個 await xxxx 都會對應生成是一個 TaskAwaiter<int> awaiter,然后 xxxx.GetAwaiter()

  C. 判斷狀態機是否執行完if (!awaiter.IsCompleted),沒有執行完的話走 <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine); 代表釋放當前線程

  D. 執行完后走,<>s__1 = awaiter.GetResult(); 拿到返回值,繼續走后面的代碼。

(此處寫的比較抽象,看下面3 結合代碼編譯再分析)

 

3. 結合代碼編譯分析

前提:准備1個Index方法,我們把它當做主方法,在該方法里面調用 F1Async-F5Async這五個方法. (要補充截圖這里)

代碼分享:

 public class Home2Controller : Controller
    {

        /// <summary>
        /// 該方法為主方法,用於調用下面的F1-F5的方法
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> Index()
        {
            await F1Async();
            await F2Async();
            await F3Async();
            await F4Async();
            await F5Async();

            return View();
        }

        /// <summary>
        /// 沒有加async和await的方法
        /// (也是一個計算密集型的異步方法,只是編譯的時候本身不會被編譯成狀態機)
        /// </summary>
        /// <returns></returns>
        public static Task<int> F1Async()
        {
            return Task.Run(() =>
            {
                return 2;
            });
        }

        /// <summary>
        /// 只要標記了async 就會被編譯成狀態機
        /// 如果方法聲明為 async,那么可以直接 return 具體的值,不再用創建Task,由編譯器創建 Task: 
        /// </summary>
        /// <returns></returns>
        public static async Task<int> F2Async()
        {
            return 2;
        }

        /// <summary>
        /// 計算密集型的異步方法
        /// (方法本身也會被編譯成狀態機)
        /// </summary>
        /// <returns></returns>
        public static async Task<int> F3Async()
        {
            return await Task.Run(() =>
            {
                return 2;
            });
        }

        /// <summary>
        /// I/O密集型的異步方法
        /// </summary>
        /// <returns></returns>
        public async Task<int> F4Async()
        {
            AsyncDBContext context = new AsyncDBContext();
            for (int i = 0; i < 10000; i++)
            {
                UserInfor uInfor = new UserInfor()
                {
                    id = Guid.NewGuid().ToString("N"),
                    userName = "ypf",
                    addTime = DateTime.Now
                };
                await context.AddAsync(uInfor);
            }
            return await context.SaveChangesAsync();
        }


      /// <summary>
      /// 沒有創建狀態機,但是new 了1個新的 task
      /// </summary>
      /// <returns></returns>
       public static Task<int> F5Async()
        {
            //內部是new Task<TResult>(result)
            return Task.FromResult(3);
        }

    }
View Code

 (1).F1Async:沒有加async和await,但它本身也是一個計算密集型的異步方法,該方法本身不會被編譯成狀態機,但調用它的方法Index會被編譯成狀態機。

 (2).F2Async:只加了async,會生成狀態機,但由於沒有加await所以不會涉及到中間狀態的變化,從-1默認狀態  變為 結束的-2狀態。

代碼分享:

 1   public class Home2Controller : Controller
 2     {
 3 
 4         /// <summary>
 5         /// 該方法為主方法,用於調用下面的F1-F5的方法
 6         /// </summary>
 7         /// <returns></returns>
 8         public async Task<IActionResult> Index()
 9         {
10             await F1Async();
11             await F2Async();
12             await F3Async();
13             await F4Async();
14             await F5Async();
15 
16             return View();
17         }
18 
19         /// <summary>
20         /// 沒有加async和await的方法
21         /// (也是一個計算密集型的異步方法,只是編譯的時候本身不會被編譯成狀態機)
22         /// </summary>
23         /// <returns></returns>
24         public static Task<int> F1Async()
25         {
26             return Task.Run(() =>
27             {
28                 return 2;
29             });
30         }
31 
32         /// <summary>
33         /// 只要標記了async 就會被編譯成狀態機
34         /// 如果方法聲明為 async,那么可以直接 return 具體的值
35         /// <returns></returns>
36         public static async Task<int> F2Async()
37         {
38             return 2;
39         }
40 
41         /// <summary>
42         /// 計算密集型的異步方法
43         /// (方法本身也會被編譯成狀態機)
44         /// </summary>
45         /// <returns></returns>
46         public static async Task<int> F3Async()
47         {
48             return await Task.Run(() =>
49             {
50                 return 2;
51             });
52         }
53 
54         /// <summary>
55         /// I/O密集型的異步方法
56         /// </summary>
57         /// <returns></returns>
58         public async Task<int> F4Async()
59         {
60             AsyncDBContext context = new AsyncDBContext();
61             for (int i = 0; i < 10000; i++)
62             {
63                 UserInfor uInfor = new UserInfor()
64                 {
65                     id = Guid.NewGuid().ToString("N"),
66                     userName = "ypf",
67                     addTime = DateTime.Now
68                 };
69                 await context.AddAsync(uInfor);
70             }
71             return await context.SaveChangesAsync();
72         }
73 
74 
75       /// <summary>
76       /// 沒有創建狀態機,但是new 了1個新的 task
77       /// </summary>
78       /// <returns></returns>
79        public static Task<int> F5Async()
80         {
81             //內部是new Task<TResult>(result)
82             return Task.FromResult(3);
83         }
84 
85     }
View Code

核心代碼剖析:

 

 (3).F3Async:既有async也有await (await只有1個),該方法是使用了Task.Run,我們把它歸為計算型的異步方法。

代碼分享:

        [AsyncStateMachine(typeof(<F3Async>d__3))]
    [DebuggerStepThrough]
    public static Task<int> F3Async()
    {
        <F3Async>d__3 stateMachine = new <F3Async>d__3();
        stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
        stateMachine.<>1__state = -1;
        AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
        <>t__builder.Start(ref stateMachine);
        return stateMachine.<>t__builder.Task;
    }
        [CompilerGenerated]
    private sealed class <F3Async>d__3 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<int> <>t__builder;

        private int <>s__1;

        private TaskAwaiter<int> <>u__1;

        private void MoveNext()
        {
            int num = <>1__state;
            int result;
            try
            {
                TaskAwaiter<int> awaiter;
                if (num != 0)
                {
                    awaiter = Task.Run(() => 2).GetAwaiter();
                    if (!awaiter.IsCompleted)
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter;
                        <F3Async>d__3 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted(ref awaiter, ref stateMachine);
                        return;
                    }
                }
                else
                {
                    awaiter = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                }
                <>s__1 = awaiter.GetResult();
                result = <>s__1;
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <>t__builder.SetResult(result);
        }

        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);
        }
    }
View Code

核心代碼剖析:

 (4).F4Async:既有async又有await,且兩個await,兩個await按照順序執行。

代碼分享:

        [AsyncStateMachine(typeof(<F4Async>d__4))]
    [DebuggerStepThrough]
    public Task<int> F4Async()
    {
        <F4Async>d__4 stateMachine = new <F4Async>d__4();
        stateMachine.<>4__this = this;
        stateMachine.<>t__builder = AsyncTaskMethodBuilder<int>.Create();
        stateMachine.<>1__state = -1;
        AsyncTaskMethodBuilder<int> <>t__builder = stateMachine.<>t__builder;
        <>t__builder.Start<<F4Async>d__4>(ref stateMachine);
        return stateMachine.<>t__builder.get_Task();
    }
[CompilerGenerated]
    private sealed class <F4Async>d__4 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<int> <>t__builder;

        public Home2Controller <>4__this;

        private AsyncDBContext <context>5__1;

        private int <i>5__2;

        private UserInfor <uInfor>5__3;

        private int <>s__4;

        private ValueTaskAwaiter<EntityEntry<UserInfor>> <>u__1;

        private TaskAwaiter<int> <>u__2;

        private void MoveNext()
        {
        
            int num = <>1__state;
            int result;
            try
            {
                ValueTaskAwaiter<EntityEntry<UserInfor>> awaiter;
                if (num == 0)
                {
                    awaiter = <>u__1;
                    <>u__1 = default(ValueTaskAwaiter<EntityEntry<UserInfor>>);
                    num = (<>1__state = -1);
                    goto IL_00e8;
                }
                if (num != 1)
                {
                    <context>5__1 = new AsyncDBContext();
                    <i>5__2 = 0;
                    goto IL_010a;
                }
                TaskAwaiter<int> awaiter2 = <>u__2;
                <>u__2 = default(TaskAwaiter<int>);
                num = (<>1__state = -1);
                goto IL_0188;
                IL_00e8:
                awaiter.GetResult();
                <uInfor>5__3 = null;
                <i>5__2++;
                goto IL_010a;
                IL_010a:
                if (<i>5__2 < 10000)
                {
                    <uInfor>5__3 = new UserInfor
                    {
                        id = Guid.NewGuid().ToString("N"),
                        userName = "ypf",
                        addTime = DateTime.Now
                    };
                    awaiter = ((DbContext)<context>5__1).AddAsync<UserInfor>(<uInfor>5__3, default(CancellationToken)).GetAwaiter();
                    if (!awaiter.get_IsCompleted())
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter;
                        <F4Async>d__4 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<ValueTaskAwaiter<EntityEntry<UserInfor>>, <F4Async>d__4>(ref awaiter, ref stateMachine);
                        return;
                    }
                    goto IL_00e8;
                }
                awaiter2 = ((DbContext)<context>5__1).SaveChangesAsync(default(CancellationToken)).GetAwaiter();
                if (!awaiter2.get_IsCompleted())
                {
                    num = (<>1__state = 1);
                    <>u__2 = awaiter2;
                    <F4Async>d__4 stateMachine = this;
                    <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <F4Async>d__4>(ref awaiter2, ref stateMachine);
                    return;
                }
                goto IL_0188;
                IL_0188:
                <>s__4 = awaiter2.GetResult();
                result = <>s__4;
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <context>5__1 = null;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <context>5__1 = null;
            <>t__builder.SetResult(result);
        }

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

核心代碼剖析:

 (5).F5Async:沒有async和await,沒有創建狀態機,但是new 了1個新的 task。

 (6).Index:多個await,通過goto 一步一步跳轉,按順序執行。

代碼分享:

[CompilerGenerated]
    private sealed class <Index>d__0 : IAsyncStateMachine
    {
        public int <>1__state;

        public AsyncTaskMethodBuilder<IActionResult> <>t__builder;

        public Home2Controller <>4__this;

        private TaskAwaiter<int> <>u__1;

        private void MoveNext()
        {
            
            int num = <>1__state;
            IActionResult result;
            try
            {
                TaskAwaiter<int> awaiter5;
                TaskAwaiter<int> awaiter4;
                TaskAwaiter<int> awaiter3;
                TaskAwaiter<int> awaiter2;
                TaskAwaiter<int> awaiter;
                switch (num)
                {
                default:
                    awaiter5 = F1Async().GetAwaiter();
                    if (!awaiter5.get_IsCompleted())
                    {
                        num = (<>1__state = 0);
                        <>u__1 = awaiter5;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter5, ref stateMachine);
                        return;
                    }
                    goto IL_0091;
                case 0:
                    awaiter5 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_0091;
                case 1:
                    awaiter4 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_00f3;
                case 2:
                    awaiter3 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_0155;
                case 3:
                    awaiter2 = <>u__1;
                    <>u__1 = default(TaskAwaiter<int>);
                    num = (<>1__state = -1);
                    goto IL_01bd;
                case 4:
                    {
                        awaiter = <>u__1;
                        <>u__1 = default(TaskAwaiter<int>);
                        num = (<>1__state = -1);
                        break;
                    }
                    IL_01bd:
                    awaiter2.GetResult();
                    awaiter = F5Async().GetAwaiter();
                    if (!awaiter.get_IsCompleted())
                    {
                        num = (<>1__state = 4);
                        <>u__1 = awaiter;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter, ref stateMachine);
                        return;
                    }
                    break;
                    IL_0091:
                    awaiter5.GetResult();
                    awaiter4 = F2Async().GetAwaiter();
                    if (!awaiter4.get_IsCompleted())
                    {
                        num = (<>1__state = 1);
                        <>u__1 = awaiter4;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter4, ref stateMachine);
                        return;
                    }
                    goto IL_00f3;
                    IL_0155:
                    awaiter3.GetResult();
                    awaiter2 = <>4__this.F4Async().GetAwaiter();
                    if (!awaiter2.get_IsCompleted())
                    {
                        num = (<>1__state = 3);
                        <>u__1 = awaiter2;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter2, ref stateMachine);
                        return;
                    }
                    goto IL_01bd;
                    IL_00f3:
                    awaiter4.GetResult();
                    awaiter3 = F3Async().GetAwaiter();
                    if (!awaiter3.get_IsCompleted())
                    {
                        num = (<>1__state = 2);
                        <>u__1 = awaiter3;
                        <Index>d__0 stateMachine = this;
                        <>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<int>, <Index>d__0>(ref awaiter3, ref stateMachine);
                        return;
                    }
                    goto IL_0155;
                }
                awaiter.GetResult();
                result = <>4__this.View();
            }
            catch (Exception exception)
            {
                <>1__state = -2;
                <>t__builder.SetException(exception);
                return;
            }
            <>1__state = -2;
            <>t__builder.SetResult(result);
        }

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

核心代碼剖析:

 

4. 重點比較一下:F1Async 和 F3Async 的區別

 (1).F1Async和F3Async都是異步方法,在外層Index方法中調用的時候,都要加await,就外層而言都不會卡主線程,外層方法都會被編譯成狀態機。

 (2).從編譯的角度而言 F1Async 方法本身不會被編譯成狀態機,F3Async方法本身會被編譯成狀態機。

 

5. 再次擴展

 (1).等待的時候要用 await xxxAsync, 而不要用 xxxAsync.wait() 和 .Result

 (2).等待多個用 await Task.WhenAll 而不要用 Task.WaitAll

原因?

 后者是同步寫法啊,阻塞線程的,從上面的編譯的源碼可以看出來,沒有await不會生成TaskAwaiter<int> awaiter。

 

 

二. 幾個用法

 1. 異常捕獲

 代碼1

public static async void EmailAsync() {
    List<string> addrs = new List<string>();
    IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
    try {
        await Task.WhenAll(asyncOps);
    } catch (AggregateException ex) {
        // 可以通過 InnerExceptions 來得到內部返回的異常
        var exceptions = ex.InnerExceptions;
        // 也可以使用 Handle 對每個異常進行處理
        ex.Handle(innerEx => {
            // 此處的演示僅僅為了說明 ex.Handle 可以對異常進行單獨處理
            // 實際項目中不一定會拋出此異常

            if (innerEx is OperationCanceledException oce) {
                // 對 OperationCanceledException 進行單獨的處理
                return true;
            } else if (innerEx is UnauthorizedAccessException uae) {
                // 對 UnauthorizedAccessException 進行單獨處理
                return true;
            }
            return false;
        });
    }
}

代碼2

public static async void EmailAsync() {
    List<string> addrs = new List<string>();
    IEnumerable<Task> asyncOps = addrs.Select(addr => SendMailAsync(addr));
    try {
        await Task.WhenAll(asyncOps);
    } catch (AggregateException ex) {
        // 此處可以針對每個任務進行更加具體的管理
        foreach (Task<string> task in asyncOps) {
            if (task.IsCanceled) {
            }else if (task.IsFaulted) {
            }else if (task.IsCompleted) {
            }
        }
    }
}

代碼3

try
{
    HttpClient hc = new HttpClient();
    var task1 = hc.GetStringAsync(textBox1.Text);
    var task2 = hc.GetStringAsync(textBox2.Text);
    var task3 = hc.GetStringAsync(textBox3.Text);
    Task.WaitAll(task1, task2, task3);
    label1.Text = task1.Result.Length.ToString();
    label2.Text = task2.Result.Length.ToString();
    label3.Text = task3.Result.Length.ToString();
}
catch (AggregateException ae)
{
    MessageBox.Show(ae.GetBaseException().ToString());
}

 

 

 

 

 

 

 

!

  • 作       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 如有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM