天天看點

C#中的異步程式設計--探索await與async關鍵字的奧妙之處,原來了解和使用異步程式設計可以這麼簡單

前言

await與async是C#5.0推出的新文法,關于await與async有很多文章講解。但看完後有沒有這樣一種感覺,感覺這東西像是不錯,但好像就是看不太懂,也不清楚該怎麼使用。雖然偶有接觸,但是一直都沒有真正搞明白。

我也是才剛剛摸索明白,把學習結果和大家探讨一下看掌握得對不對。個人的學習習慣就是,有複雜的東西可以簡單說明白,就會分享出來~

(閱讀本文需要具備多線程及任務程式設計的基礎)

重點

在學習async/await最難的是什麼呢?就是了解它的工作方式!

1.所有的async方法傳回類型必然是Task或Task<T>,這是異步處理的基礎!

2.在async方法中遇到await關鍵字後,目前線程立即傳回(到調用方),繼續之前的處理邏輯;await關鍵字之後的代碼邏輯,将交由新的線程處理;當新的線程處理完成後,可以從新的線程傳回處理結果到調用(處)線程當中,結束等待。

3.在一個async方法中,會根據await關鍵字進行分割,拆分到不同的線程處理同一個方法的不同部分!

4.把一個方法代碼的不同部分拆分到多個線程處理,這是異步方法和同步方法的最大不同!

把上面幾點搞明白了,其實異步程式設計也就大概清楚了吧。。

簡單異步調用

C#中的異步程式設計--探索await與async關鍵字的奧妙之處,原來了解和使用異步程式設計可以這麼簡單
class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}->Main.異步方法執行前", Thread.CurrentThread.ManagedThreadId.ToString());//輸出異步處理之前的線程ID
            DoAsync(1000).Wait();//執行異步處理,并等待該異步方法執行完成後才繼續
            Console.WriteLine("{0}->Main.異步方法執行後", Thread.CurrentThread.ManagedThreadId.ToString());//輸出異步處理之後的線程ID

            Console.Read();
        }

        /// <summary>
        /// 執行異步處理
        /// </summary>
        /// <param name="times">模拟處理時長</param>
        /// <returns></returns>
        public static async Task DoAsync(int times)
        {
            Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//輸出調用線程ID
            await Task.Run(() => Thread.Sleep(times));///執行一個異步任務,并等待傳回結果才繼續;需要注意的是,調用線程執行到這一行的時候其實就已經傳回了
            Console.WriteLine("{0}->DoAsync.await之後", Thread.CurrentThread.ManagedThreadId.ToString());//異步操作執行完了,但這裡已經是新的線程了
        }
    }      
C#中的異步程式設計--探索await與async關鍵字的奧妙之處,原來了解和使用異步程式設計可以這麼簡單
1->Main.異步方法執行前
1->DoAsync.await之前
3->DoAsync.await之後
1->Main.異步方法執行後      

請留意異步方法DoAsync的處理,在await之前和await之後,已經是兩個不一樣的線程ID。

也就是說,一個方法内部被拆分成了多個部分,不同的部分分别由不同的線程處理。

有傳回值的異步調用

C#中的異步程式設計--探索await與async關鍵字的奧妙之處,原來了解和使用異步程式設計可以這麼簡單
class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("{0}->Main.異步方法執行前", Thread.CurrentThread.ManagedThreadId.ToString());//輸出異步處理之前的線程ID
            var asyncTask = DoAsync(1000);//異步執行處理
            Console.WriteLine("{0}->Main.異步方法執行後", Thread.CurrentThread.ManagedThreadId.ToString());//輸出異步處理之後的線程ID
            var result = asyncTask.Result;//等待異步處理完成才繼續
            Console.WriteLine("{0}->Main.異步方法完成後", Thread.CurrentThread.ManagedThreadId.ToString());//輸出異步完成之後的線程ID

            Console.Read();
        }

        /// <summary>
        /// 執行異步處理
        /// </summary>
        /// <param name="times">模拟處理時長</param>
        /// <returns></returns>
        public static async Task<int> DoAsync(int times)
        {
            Console.WriteLine("{0}->DoAsync.await之前", Thread.CurrentThread.ManagedThreadId.ToString());//輸出調用線程ID
            var result = await Task.Run(() => { Thread.Sleep(times); return times; });///執行一個異步任務,并等待傳回結果才繼續;需要注意的是,調用線程執行到這一行的時候就已經傳回了
            Console.WriteLine("{0}->DoAsync.await之後", Thread.CurrentThread.ManagedThreadId.ToString());//異步操作執行完了,但這裡已經是新的線程了
            return result;//傳回計算結果,注意:傳回結果時和進入方法時的線程已經不一樣了
        }
    }      
C#中的異步程式設計--探索await與async關鍵字的奧妙之處,原來了解和使用異步程式設計可以這麼簡單
1->Main.異步方法執行前
1->DoAsync.await之前
1->Main.異步方法執行後
3->DoAsync.await之後
1->Main.異步方法完成後      

請注意:在同步方法Main中執行的時候都是同一個線程;在異步方法DoAsync執行的時候,在await之前是調用線程,在await之後則是另一個線程。

總結

在異步(async)方法執行中,會根據await關鍵字,拆分一個方法為多個部分,分别由不同的線程執行。

在異步(async)方法執行中,遇到await關鍵字,調用線程會立即傳回(線程池)繼續後續的處理邏輯;而後,調用方可以使用Task.Wait()或Task<T>.Result進行阻塞,等待異步方法執行完畢再繼續。

在異步(async)方法執行後,若不使用Task.Wait()進行等待,或不使用Task<T>.Result擷取傳回結果,則該方法将僅以異步方式執行。

那麼異步方法的好處到底在哪?一下子,我也說不上來。。因為好像隻用Task,也可以達到類似的效果,雖然也有差別。