常見的異步方式async 和 await

以前研究過c#的async和await關鍵字,幕後幹了什麼,可是不知道爲何找不到相關資料了。如今從新研究一遍,順便記錄下來,方便之後查閱。程序員

基礎知識

async 關鍵字標註一個方法,該方法返回值是一個Task、或者Task<TResult>、void、包含GetAwaiter方法的類型。該方法一般包含一個await表達式。該表達式標註一個點,將被某個異步方法回跳到該點。而且,當前函數執行到該點,將馬上返回控制權給調用方。c#

以上描述了async方法想幹的事情,至於如何實現,這裏就不涉獵了。多線程

我的看法

由此能夠知道,async 和await關鍵字主要目的是爲了控制異步線程的同步,讓一個異步過程,表現得好像同步過程同樣。併發

好比async 方法分n個任務去下載網頁並進行處理:先await下載,而後馬上返回調用方,以後的處理就由異步線程完成下載後調用。這時候調用方能夠繼續執行它的任務,不過,若是調用方馬上就須要async的結果,那麼應該就只能等待,不過大多數狀況:他暫時不須要這個結果,那麼就能夠並行處理這些代碼。異步

可見,並行性體如今await 上,若是await 點和最終的數據結果距離越遠,那麼並行度就越高。若是await的點越多,相信也會改善並行性。async

資料顯示,async 和await 關鍵字並不會建立線程,這是很關鍵的一點。他們只是建立了一個返回點,提供給須要他的線程使用。那麼線程到底是誰建立?注意await 表達式的組成,他須要一個Task,一個Task並不表明必定要建立線程,也能夠是另外一個async方法,可是層層包裹最裏面的方法,極可能就是一個原生的Task,好比await Task.Run(()=>Thread.Sleep(0)); ,這個真正產生線程的語句,就會根據前面那些await點,逐個回調。函數

從這點來看,async 方法,未必就是一個異步方法,他在語義上更加貼近「非阻塞」, 當遇到阻塞操做,馬上用await定點返回,至於其餘更深一層的解決手段,它就不關心了。這是程序員須要關心的,程序員須要用真正的建立線程代碼,來完成異步操做(固然這一步可由庫程序員完成)。高併發

注意async的幾個返回值類型,這表明了不一樣的使用場景。若是是void,說明客戶端不關心數據同步問題,它只須要線程的控制權馬上返回。能夠用在ui 等場合,若是是Task,客戶端也不關心數據,可是它但願可以控制異步線程,這多是對任務執行順序有必定的要求。固然,最多見的是Task<TResult>。oop

綜上,async和await並非爲了多任務而設計的,若是追求高併發,應該在async函數內部用Task好好設計一番。在使用async 和await的時候,只須要按照非阻塞的思路去編寫代碼就能夠了,至於幕後怎麼處理就交給真正的多線程代碼建立者吧。ui

示範代碼

        static async Task RunTaskAsync(int step)
        {
            for(int i=0; i < step; i++)
            {
                await Task.Run(()=>Thread.Sleep(tmloop));//點是靜態的,依次執行
                Thread.Sleep(tm2);
            }
            Thread.Sleep(tm3);
        }

//客戶端
            Task tk= RunTaskAsync(step);
            Thread.Sleep(tm1);//這一段是並行的,取max(函數,代碼段)最大時間
            tk.Wait( );//這裏表明最終數據

爲了達到高度並行,應該用真正的多線程代碼:

        static async Task RunTaskByParallelAsync(int step)
        {
            await Task.Run(()=>Parallel.For(0,step,
                s=>{loop(tmloop);
                    loop(tm2);
                    }
            ));
            loop(tm3);
        }

並行編碼方法

並行執行有幾個方法,第一個是建立n個Task,一塊兒啓動。問題是怎麼處理await點。每一個task寫一個await點是不行的,由於遇到第一個await就馬上返回,而不會開啓全部任務並行執行。所以await不能隨便放。那麼如何爲一組Task設定await點呢?能夠經過Task.WhenAll 這個方法,他會等待一組Task執行完畢返回。

特定狀況下,能夠用Parallel.For 來開啓一組任務,可是這個類並無實現async模式,也就是它會阻塞當前線程,因此須要用一個Task來包裹它。

可見,非阻塞和並行不徹底是一回事。

相關文章
相關標籤/搜索