很開心今天能與大家一起聊聊C# 8.0中的新特性-<code>Async Streams</code>,一般人通常看到這個詞表情是這樣.

簡單說,其實就是C# 8.0中支援<code>await foreach</code>.
或者說,C# 8.0中支援異步傳回枚舉類型<code>async Task<IEnumerable<T>></code>.
好吧,還不懂?Good,這篇文章就是為你寫的,看完這篇文章,你就能明白它的神奇之處了.
<code>Async Streams</code>這個功能已經釋出很久了,在去年的Build 2018 The future of C#就有示範,最近VS 2019釋出,在該版本的Release Notes中,我再次看到了這個新特性,因為對異步程式設計不太熟悉,是以借着這個機會,學習新特性的同時,把異步程式設計重溫一遍.
本文内容,參考了<code>Bassam Alugili</code>在InfoQ中發表的Async Streams in C# 8,撰寫本部落格前我已聯系上該作者并得到他支援.
C# 5 引入了 Async/Await,用以提高使用者界面響應能力和對 Web 資源的通路能力。換句話說,異步方法用于執行不阻塞線程并傳回一個标量結果的異步操作。
微軟多次嘗試簡化異步操作,因為 Async/Await 模式易于了解,是以在開發人員當中獲得了良好的認可。
詳見The Task asynchronous programming model in C#
要了解問什麼需要<code>Async Streams</code>,我們先來看看這樣的一個示例,求出5以内的整數的和.
調用方法.
輸出結果.
可以看到,整個過程就一個線程Id為1的線程自上而下執行,這是最基礎的做法.
接下來,我們使用yield運算符使得這個方法程式設計延遲加載,如下所示.
主函數
運作結果如下.
正如你在輸出視窗中看到的那樣,結果被分成幾個部分傳回,而不是作為一個值傳回。以上顯示的累積結果被稱為惰性枚舉。但是,仍然存在一個問題,即 sum 方法阻塞了代碼的執行。如果你檢視線程ID,可以看到所有東西都在主線程1中運作,這顯然不完美,繼續改造.
我們試着将async用于SumFromOneToCount方法(沒有yield關鍵字).
主函數.
運作結果.
我們可以看到計算過程是在另一個線程中運作,但結果仍然是作為一個值傳回!任然不完美.
如果我們想把惰性枚舉(yield return)與異步方法結合起來,即傳回Task<IEnumerable,這怎麼實作呢?
我們根據假設把代碼改造一遍,使用<code>Task<IEnumerable<T>></code>來進行計算.
可以看到,直接出現錯誤.
其實,在C# 8.0中Task<IEnumerable>這種組合稱為IAsyncEnumerable。這個新功能為我們提供了一種很好的技術來解決拉異步延遲加載的問題,例如從網站下載下傳資料或從檔案或資料庫中讀取記錄,與 IEnumerable 和 IEnumerator 類似,Async Streams 提供了兩個新接口 IAsyncEnumerable 和 IAsyncEnumerator,定義如下:
下面,我們就來見識一下AsyncStrema的威力,我們使用IAsyncEnumerable來對函數進行改造,如下.
如果一切順利,那麼就能看到這樣的運作結果了.
最後,看到這就是我們想要的結果,在枚舉的基礎上,進行了異步疊代.
可以看到,整個計算過程并沒有造成主線程的阻塞,其中,值得重點關注的是紅色方框區域的<code>線程5</code>!<code>線程5</code>!<code>線程5</code>!線程5在請求下一個結果後,并沒有等待結果傳回,而是去了Main()函數中做了别的事情,等待請求的結果傳回後,線程5又接着執行foreach中任務.
如果還沒有了解<code>Async Streams</code>的好處,那麼我借助用戶端 / 伺服器端架構是示範這一功能優勢的絕佳方法。
用戶端向伺服器端發送請求,用戶端必須等待(用戶端被阻塞),直到伺服器端做出響應.
示例中Yield Return就是以這種方式執行的,是以整個過程隻有一個線程即線程1在處理.
用戶端發出資料塊請求,然後繼續執行其他操作。一旦資料塊到達,用戶端就處理接收到的資料塊并詢問下一個資料塊,依此類推,直到達到最後一個資料塊為止。這正是 Async Streams 想法的來源。
最後一個示例就是以這種方式執行的,<code>線程5</code>詢問下一個資料後并沒有等待結果傳回,而是去做了Main()函數中的别的事情,資料到達後,<code>線程5</code>又繼續處理foreach中的任務.
如果你使用的是<code>.net core 2.2</code>及以下版本,會遇到這樣的報錯.
需要安裝<code>.net core 3.0 preview</code>的SDK(截至至部落格撰寫日期4月9日,<code>.net core SDK</code>最新版本為<code>3.0.100-preview3-010431</code>),安裝好SDK後,如果你是VS 2019正式版,可能無法選擇<code>.net core 3.0</code>,vs 2019 正式版預設情況下沒有開啟對預覽版<code>.net core 3.0</code>的支援.
根據網友補充,需要在VS 2019正式版本中需要開啟<code>使用 .Net core SDK 預覽版</code>,才能建立3.0的項目.
工具 > 選項 > 項目和解決方案 > .Net Core > 使用 .Net core SDK 預覽版
總結
我們已經讨論過 <code>Async Streams</code>,它是一種出色的異步拉取技術,可用于進行生成多個值的異步計算。
<code>Async Streams</code> 背後的程式設計概念是異步拉取模型。我們請求擷取序列的下一個元素,并最終得到答複。Async Streams 提供了一種處理異步資料源的絕佳方法,希望對大家能夠有所幫助。
文章中涉及的所有代碼已儲存在我的GitHub中,請盡情享用!
https://github.com/liuzhenyulive/AsyncStreamsInCShaper8.0
緻謝
之前一直感覺國外的大師級開發者遙不可及甚至高高在上,在遇到<code>Bassam Alugili</code>之後,我才真正感受到技術交流沒有高低貴賤,正如他對我說的 <code>The most important thing in this world is sharing the knowledge!</code>
Thank you,I will keep going!!
參考文獻: Async Streams in C# 8 https://www.infoq.com/articles/Async-Streams