異步async/await簡單應用與探究

感謝Marco CAO指出的兩點錯誤,已作出修改與補充c++

異步函數(async/await)簡單應用

.NET Framework4.5提供了針對異步函數語法糖,簡化了編寫異步函數的複雜度。編程

下面經過一個簡單的示例,介紹.NET Framework4.5對異步函數的支持。網絡

窗體頁面異步

窗體代碼async

    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private async void btnGetText_Click(object sender, EventArgs e)
        {
            string urlString = string.Empty;
            using (HttpClient client = new HttpClient())
            {
                //異步獲取「http://10.32.112.82/epms/」內容(線程返回)。用戶界面依舊能夠操做
                urlString = await client.GetStringAsync(@"http://10.32.112.82/epms/");
            }
            txtUrlString.Text = urlString;
        }
    }
View Code

說明ide

1.點擊獲取url,程序開始獲取url內容。(注意:這裏我寫的url是咱們公司內部的網址,必須經過代理才能夠訪問,因此確定會有延遲效果。異步編程

2.雖然有延遲效果,可是窗體依舊能夠操做(在第一個textBox中能夠輸入內容)。代表窗體並無被阻塞,也就是說獲取url內容是異步操做。函數

3.一段時間後,你會發現窗體報錯,代表獲取url內容操做完畢,操做失敗。工具

講解測試

1.異步函數的關鍵字:async;等待異步結果的關鍵字:await

2.異步方法的調用,與獲取異步方法的結果,在同一個方法體內。這點極大了簡化異步方法維護的成本。(之前BeginInvoke()、EndInvoke(),起碼是2個方法,且不能在同一方法體內調用。下文異步函數簡化APM模型有介紹。)

3.await做用的方法表示:【進入該方法的線程】會返回線程池,程序會在此掛起等待返回結果(這裏須要注意,由於程序獲得返回結果再執行時,是從線程池中另起了一個線程在執行,同時【子線程】會執行await後面的方法(這點是:async與await的核心)

異步函數(async/await)實現異步原理(狀態機)

async/await關鍵點:

1.有幾個async方法,就建立幾個【狀態機】。

2.狀態機】的幾種狀態(-1:初始化;-2:方法體執行結束;0:第一個await方法;1:第二個await方法;。。。依次往下

3.【狀態機】控制着異步方法執行流程(MoveNext方法

經過上面的介紹,咱們知道【狀態機控制着方法執行流程,下面咱們就來對【狀態機】如何控制方法執行流程一探究竟。

下面這段測試代碼,來自:CLR via C#(第4版)

示例代碼

        private static async Task<string> MyMethodAsync(int argument)
        {
            int local = argument;
            try
            {
                Type1 result1 = await Method1Async();

                for (int x = 0; x < 3; x++)
                {
                    Type2 result2 = await Method2Async();
                }
            }
            catch (Exception)
            {
                Console.WriteLine("Catch");
            }
            finally
            {
                Console.WriteLine("Finally");
            }
            return "Done";
        }

        private static async Task<Type1> Method1Async()
        {
            string a = await Task.Run<string>(() => "Method1");
            return new Type1();
        }

        private static async Task<Type2> Method2Async()
        {
            string a = await Task.Run<string>(() => "Method2");
            return new Type2();
        }

        sealed class Type1 { }
        sealed class Type2 { }
View Code

用Reflector.exe反編譯後,獲得以下代碼(MyMethodAsync)

[CompilerGenerated]
private struct <MyMethodAsync>d__9 : IAsyncStateMachine
{
    // Fields
    public int <>1__state;
    public AsyncTaskMethodBuilder<string> <>t__builder;
    private object <>t__stack;
    private TaskAwaiter<Program.Type1> <>u__$awaitere;
    private TaskAwaiter<Program.Type2> <>u__$awaiterf;
    public int <local>5__a;
    public Program.Type1 <result1>5__b;
    public Program.Type2 <result2>5__d;
    public int <x>5__c;
    public int argument;

    // Methods
    private void MoveNext()
    {
        string str;
        try
        {
            bool flag = true;
            switch (this.<>1__state)
            {
                case -3:
                    goto Label_01E1;

                case 0:
                case 1:
                    break;

                default:
                    this.<local>5__a = this.argument;
                    break;
            }
            try
            {
                switch (this.<>1__state)
                {
                }
                try
                {
                    TaskAwaiter<Program.Type1> awaiter;
                    switch (this.<>1__state)
                    {
                        case 0:
                            break;

                        case 1:
                            goto Label_0137;

                        default:
                            awaiter = Program.Method1Async().GetAwaiter();
                            if (awaiter.IsCompleted)
                            {
                                goto Label_00D7;
                            }
                            this.<>1__state = 0;
                            this.<>u__$awaitere = awaiter;
                            this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type1>, Program.<MyMethodAsync>d__9>(ref awaiter, ref this);
                            flag = false;
                            return;
                    }
                    awaiter = this.<>u__$awaitere;
                    this.<>u__$awaitere = new TaskAwaiter<Program.Type1>();
                    this.<>1__state = -1;
                Label_00D7:
                    Program.Type1 introduced11 = awaiter.GetResult();
                    awaiter = new TaskAwaiter<Program.Type1>();
                    Program.Type1 type = introduced11;
                    this.<result1>5__b = type;
                    this.<x>5__c = 0;
                    while (this.<x>5__c < 3)
                    {
                        TaskAwaiter<Program.Type2> awaiter3 = Program.Method2Async().GetAwaiter();
                        if (awaiter3.IsCompleted)
                        {
                            goto Label_0156;
                        }
                        this.<>1__state = 1;
                        this.<>u__$awaiterf = awaiter3;
                        this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter<Program.Type2>, Program.<MyMethodAsync>d__9>(ref awaiter3, ref this);
                        flag = false;
                        return;
                    Label_0137:
                        awaiter3 = this.<>u__$awaiterf;
                        this.<>u__$awaiterf = new TaskAwaiter<Program.Type2>();
                        this.<>1__state = -1;
                    Label_0156:
                        Program.Type2 introduced12 = awaiter3.GetResult();
                        awaiter3 = new TaskAwaiter<Program.Type2>();
                        Program.Type2 type2 = introduced12;
                        this.<result2>5__d = type2;
                        this.<x>5__c++;
                    }
                }
                catch (Exception)
                {
                    Console.WriteLine("Catch");
                }
            }
            finally
            {
                if (flag)
                {
                    Console.WriteLine("Finally");
                }
            }
            str = "Done";
        }
        catch (Exception exception)
        {
            this.<>1__state = -2;
            this.<>t__builder.SetException(exception);
            return;
        }
    Label_01E1:
        this.<>1__state = -2;
        this.<>t__builder.SetResult(str);
    }

    [DebuggerHidden]
    private void SetStateMachine(IAsyncStateMachine param0)
    {
        this.<>t__builder.SetStateMachine(param0);
    }
}

 
View Code

這裏我要說一句抱歉,這裏貼出的代碼可讀性不好(沒有關鍵字,沒有着色)。原本想截圖的,可是因爲反編譯的代碼很長,沒法在我顯示器中一屏顯示完,因此我也沒截圖。下面講解的時候,採用分段截圖來講明,但願你們看得明白。同時也推薦你們本身用Reflector.exe反編譯後,本身閱讀。

代碼閱讀

1.【狀態機】初始化(-1)

2.【狀態機】Start入口-》MoveNext方法

3.【狀態機】個數(3個async方法,因此3個狀態機)

4.核心代碼(MoveNext方法)

 

這段代碼的大體意思是:<>1__state初始狀態爲-1,進入default分支,判斷Method1Async()是否執行完畢,若未執行完畢,<>1__state設計爲0(表示返回第一個await對應的方法塊),調用了一個方法(紅框標註),而後return(線程返回線程池,函數掛起等待返回結果)。若執行完畢,【goto Label_00D7】正好就是獲取第一個await結果的代碼塊。

下面重點關注一下,紅框標註的代碼(AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted())作了什麼?

依舊須要藉助反編碼工具,查到這個調用的真實代碼,以下

第一句是關鍵,方法名稱:獲得完成時調用的委託。進入方法一探究竟

AsyncTaskMethodBuilder.GetCompletionAction()代碼以下

紅框的批註,action就是完成時調用的方法,進入要MoveNextRunner.Run()方法中。

真相大白,action就是狀態機的MoveNext()方法。

總結:AsyncTaskMethodBuilder<string>.AwaitUnsafeOnCompleted()作得事就是,當異步函數執行完畢後,回調狀態機的MoveNext()方法

異步函數(async/await)簡化異步編程模型(APM)

APM編程代碼

        static void Main(string[] args)
        {
            AsyncAPM();
        }

        static void AsyncAPM()
        {
            Console.WriteLine("Main thread ID={0}", Thread.CurrentThread.ManagedThreadId);

            byte[] s_data = new byte[100];
            FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
            fs.BeginRead(s_data, 0, s_data.Length, ReadIsDone, fs);

            Console.WriteLine("主線程執行完畢");

            Console.ReadLine();
        }

        private static void ReadIsDone(IAsyncResult ar)
        {
            Thread.Sleep(5000);
            Console.WriteLine("ReadIsDone thread ID={0}", Thread.CurrentThread.ManagedThreadId);

            FileStream fs = (FileStream)ar.AsyncState;
            int bytesRead = fs.EndRead(ar);
            fs.Close();

            Console.WriteLine("Number of bytes read={0}", bytesRead);

        }
View Code

這種編程模式有一個很大的缺點:若是我想【獲取異步返回的結果,而後打印出來】。那麼【獲取異步返回結果,而後打印出來】必須放在回調函數中,也就是另外一塊代碼中(上面的實例,獲取字節數,而後打印出來。就必須放在ReadIsDone函數中),這其實給閱讀代碼增添了難度(若是多個異步函數,閱讀代碼時就要在不少方法中來回跳轉)。固然你也能夠在一個方法中用EndInvoke()來作等待。這樣雖然也是異步,可是會出現主程序阻塞,等待異步返回結果。

異步函數簡化代碼

        static void Main(string[] args)
        {
            SimplifyAsyncToAPM();
            Console.ReadLine();
        }

        static async void SimplifyAsyncToAPM()
        {
            int length = await AsyncToAPM();
            Console.WriteLine("File length={0}", length);
        }

        static async Task<int> AsyncToAPM()
        {
            byte[] s_data = new byte[100];
            FileStream fs = new FileStream(@"d:\1.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
            return await Task.Factory.FromAsync<byte[], int, int, int>(fs.BeginRead, fs.EndRead, s_data, 0, s_data.Length, fs);
        }
View Code

異步函數(async/await)應用於事件編程模型

示例代碼(讀取網絡中一張圖片的字節數)

        static void Main(string[] args)
        {
            Task<int> result = AsyncEvent(new Uri("http://image.baidu.com/search/detail?ct=503316480&z=0&ipn=d&word=%E5%9B%BE%E7%89%87&pn=0&spn=0&di=84991311930&pi=&rn=1&tn=baiduimagedetail&ie=utf-8&oe=utf-8&cl=2&lm=-1&cs=3063552411%2C3030228420&os=1484689785%2C3968535026&simid=0%2C0&adpicid=0&ln=30&fr=ala&fm=&sme=&cg=&bdtype=11&oriquery=&objurl=http%3A%2F%2Fwww.52ij.com%2Fuploads%2Fallimg%2F160317%2F1110104P8-4.jpg&fromurl=ippr_z2C%24qAzdH3FAzdH3Fooo_z%26e3Bcdt3_z%26e3Bv54AzdH3F25g2xtw5AzdH3F9cmaa0_z%26e3Bip4s&gsm=0"));
            Console.WriteLine(result.Result);
        }

        static async Task<int> AsyncEvent(Uri uri)
        {
            WebClient wc = new WebClient();
            TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();

            wc.DownloadDataCompleted += (s, e) =>
            {
                if (e.Cancelled)
                    tcs.SetCanceled();
                else if (e.Error != null)
                    tcs.SetException(e.Error);
                else
                    tcs.SetResult(e.Result.Count());
            };

            wc.DownloadDataAsync(uri);

            return await tcs.Task;
        }    
View Code

感謝你們的耐心閱讀

相關文章
相關標籤/搜索