C# .net async await 學習

 async/await簡單介紹

在處理比較耗時的操做(如圖片處理、數據壓縮、http請求等)傳統的異步方法是直接使用Thread或者Task進行操做,在複雜的應用編寫中可能會出現回調的問題,所以C#目前主要推薦使用async/await來進行異步操做。也就是async/await主要用來異步回調問題, 而真正的異步操做仍是用Task。編程

返回值

一般返回 Task 或 Task<TResult>。 在異步方法中,await 運算符應用於經過調用另外一個異步方法返回的任務安全

若是方法包含指定 TResult 類型操做數的 return 語句,將 Task<TResult> 指定爲返回類型,若是方法不含任何 return 語句或包含不返回操做數的 return 語句,將 Task 用做返回類型多線程

異步方法也能夠具備 void 返回類型。可是不推薦使用,由於沒法等待具備 void 返回類型的異步方法,而且無效返回方法的調用方捕獲不到異步方法引起的任何異常,並且也違背了咱們使用他的初衷--解決異步回調問題異步

還須要注意:異步方法既不能聲明任何 in、ref 或 out 參數,也不能具備引用返回值,但它能夠調用具備此類參數的方法async

實例

假如如今要作飯,須要作米飯2秒,作湯2秒;同步的方法就是先作米飯等待2秒,而後作湯等待2秒;異步的方法,米飯和湯同時作,一共花2秒ide

代碼以下異步編程

class Program
    {
        private static Stopwatch stopwatch = new Stopwatch();
        static void Main(string[] args)
        {
            stopwatch.Start();
            DoCook();
            Console.ReadLine();
        }
        /// <summary>
        /// Cooking
        /// </summary>
        /// <returns></returns>
        static async Task DoCook()
        {
            Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString());
            //case 1 異步Cooking
            var rice = DoRice();
            var soup = DoSoup();
            await rice;
            await soup;
            Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() } -  {(rice.Result + soup.Result).ToString()}");
        }

        /// <summary>
        /// 作米飯,能夠獨立作
        /// </summary>
        /// <returns></returns>
        static async Task<int> DoRice()
        {
            Console.WriteLine("DoRice Start: " + stopwatch.ElapsedMilliseconds.ToString());
            var rice = 0;
            await Task.Run(() =>
            {
                Thread.Sleep(2000);
                rice = 100;
            });
            Console.WriteLine("DoRice End: " + stopwatch.ElapsedMilliseconds.ToString());
            return rice;
        }

        /// <summary>
        /// 作湯,能夠獨立作
        /// </summary>
        /// <returns></returns>
        static async Task<int> DoSoup()
        {
            Console.WriteLine("DoSoup Start: " + stopwatch.ElapsedMilliseconds.ToString());
            var soup = 0;
            await Task.Run(() =>
            {
                Thread.Sleep(2000);
                soup = 100;
            });
            Console.WriteLine("DoSoup End: " + stopwatch.ElapsedMilliseconds.ToString());
            return soup;
        }
    }

 返回的結果函數

 

 如今你女友很做,她非得吃蛋炒飯,蛋炒飯必須先作飯,假如作湯2秒,作蛋炒飯2秒鐘,可是必須等米飯先作好性能

 

定義一個作蛋炒飯的方法ui

 /// <summary>
        /// 作蛋炒飯,須要先作米飯
        /// </summary>
        /// <returns></returns>
        static async Task<int> DoEggRice()
        {
            Console.WriteLine("DoEggRice Start: " + stopwatch.ElapsedMilliseconds.ToString());
            var rice = await DoRice();
            var eggRice = 0;
            await Task.Run(() =>
            {
                Thread.Sleep(2000);
                eggRice = rice + 100;
            });
            Console.WriteLine("DoEggRice End: " + stopwatch.ElapsedMilliseconds.ToString());
            return eggRice;
        }

而後開始

        /// <summary>
        /// Cooking
        /// </summary>
        /// <returns></returns>
        static async Task DoCook()
        {
            Console.WriteLine("Cook Start: " + stopwatch.ElapsedMilliseconds.ToString());//case 3 異步EggCooking
            var eggRice = DoEggRice();
            var soup = DoSoup();
            await eggRice;
            await soup;
            Console.WriteLine($"Cook End: {stopwatch.ElapsedMilliseconds.ToString() }  - {(eggRice.Result + soup.Result).ToString()}");
        }

結果以下

 

 

注意:

  1. 若某個函數F的函數體中須要使用await關鍵字的函數必須以async標記,進一步致使須要使用await調用F的那個函數也必須以async標記的狀況
  2. await 表達式:用於異步方法內部,指出須要異步執行的任務。一個異步方法能夠包含多個 await 表達式(不存在 await 表達式的話 IDE 會發出警告)
  3. var rice = DoRice();  rice.Result 屬性爲阻止屬性。 若是你在其任務完成以前嘗試訪問它,當前處於活動狀態的線程將被阻止,直到任務完成且值爲可用。 在大多數狀況下,應經過使用 await 訪問此值,而不是直接訪問屬性

引用問答

異步必定能提升效率嗎?
不必定。異步本質上仍是多線程,只是簡化多線程的實現方式。至於使用多線程編程時可否提升程序執行效率,取決於 CPU 核心數,計算任務的複雜度以及該項任務自己是否適合被切分爲並行計算模塊。過於頻繁地將不適合並行計算的任務拆分紅異步編程中去,反而會致使密集計算性能的降低,由於此時線程池會疲於應對大量的線程調度操做。
有 async 必定要有 await 嗎?

不必定。在標記爲 async 的方法中,沒必要須出現 await 關鍵字,只是若沒有 await 關鍵字,這個方法不是真正意義上的異步方法,它會與普通方法同樣是同步執行的。編譯器不會報錯,但會給出提示。

相反,若要使用 await 關鍵字,則必須在方法簽名中包含 async 關鍵字。不然 await 將被當作標識符,而不能被當作一個關鍵字來處理。也就是說,當一個方法的簽名中不包含 async 關鍵字時,你甚至能夠在方法體中把 await 做爲變量名。但這種操做是極其不推薦的,很容易形成誤導。

異步方法的名稱必定要以「Async」爲結尾嗎?

不必定。這只是習慣問題,就跟微軟推薦全部的自定義特性後面都以「Attributes」爲結尾同樣,這不是必須的,只是若是你們都這樣作了,理解起來更加方便一些。具體狀況取決於不一樣場合下的規範要求。

使用 Task 而且 Run 了以後就實現異步了嗎?

不是,這只是進行了一次多線程操做,後面的語句仍是同步執行的。直到碰見 await 關鍵字,隨着控制權的返回,才真正能實現異步。

異步是線程安全的嗎?

理論上是的,這也是爲何異步編程模型可以極大地簡化傳統多線程操做所帶來的各類問題的一大緣由。儘管 await 所指的對象運行在其餘線程上,但其後的語句仍是會在原始線程上被執行。更深層次地說,後續的語句其實是使用 Task 的 ContinueWith 方法來實現的。因此咱們大能夠放心的在異步方法中修改諸如 UI 元素等由主線程管理的資源。

可是,異步編程模型只是簡化了這個過程,而不能替代咱們解決具體的數據同步問題。若是在 await 以後有對其餘共享資源的訪問,而在 await 獲取執行結果以前,這些資源已經被其餘線程修改,那麼 await 後續語句執行時所面對的數據內容將是不可預測的。

異步必定是返回控制權與等待結果同時進行的嗎?

第一時間返回控制權是必定的,而等待與否要看任務執行的狀態。當程序遇到 await 關鍵字時,若是 Task 所指代的對象以極快的速度完成,那麼異步方法內部就會以同步執行的方式繼續向後執行 await 語句後面的操做,不會產生等待。只有當 Task 沒有執行完畢時,纔會進行等待

參考文獻

https://blog.csdn.net/qc530167365/article/details/83108848

https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/concepts/async/

https://www.jianshu.com/p/1136e79d96e6

https://www.jianshu.com/p/8ea7ed4a2493

相關文章
相關標籤/搜索