(此文章同時發(fā)表在本人微信公眾號“dotNET每日精華文章”,歡迎右邊二維碼來關注。)
題記:就語言和運行時層面,C#做并發(fā)編程一點都不弱,缺的是生態(tài)和社區(qū)。
硅谷才女朱赟(我的家門)昨天發(fā)了一篇文章《為什么用 Java —— 關于并發(fā)編程》,讓大家學習了Java中如何進行并發(fā)編程的一些基本知識。作為一個將近15年的.NET程序員,我覺得有必要給大家補充介紹一下C#進行并發(fā)編程的知識(當然不會太深入講解)。這篇文章無意進行技術比較,畢竟技術只是工具(大同小異,各有千秋),主要還是看用工具的人。
并發(fā)(英文Concurrency),其實是一個很泛的概念,字面意思就是“同時做多件事”,不過方式有所不同。在.NET的世界里面,并發(fā)一般涉及如下幾個方面:
為了支持以上編程,.NET提供了很多基礎功能,比如:委托,匿名函數(shù),Lambda表達式,線程池,Task模型,支持并發(fā)的集合(線程安全集合和不可變集合) ,調(diào)度器,同步功能。在這里,就不對這些內(nèi)容進行介紹了,大家可以自行搜索學習。另外,對于Actor模型,.NET中也有支持,但我不認為它屬于語言/運行時層面的并發(fā),它更像架構(gòu)層面的并發(fā),我最后會簡單介紹。
1,異步編程異步編程就是使用future模式(又稱promise)或者回調(diào)機制來實現(xiàn)(Non-blocking on waiting)。
如果使用回調(diào)或事件來實現(xiàn)(容易callback hell),不僅編寫這樣的代碼不直觀,很快就容易把代碼搞得一團糟。不過在.NET 4.5(C# 5)中引入的async/await關鍵字(在.NET 4.0中通過添加Microsoft.Bcl.Async包也可以使用),讓編寫異步代碼變得容易和優(yōu)雅。通過使用async/await關鍵字,可以像寫同步代碼那樣編寫異步代碼,所有的回調(diào)和事件處理都交給編譯器和運行時幫你處理了。
使用異步編程有兩個好處:不阻塞主線程(比如UI線程),提高服務端應用的吞吐量。所以微軟推薦ASP.NET中默認使用異步來處理請求。
要詳細了解異步編程,可以參考官方文檔:https://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx和《Async in C# 5.0》這本書。另外,在這個官方文檔中,微軟還特意把異步編程分作了3種不同的模型:基于任務的模式(TAP)就是我上面推薦的這種,基于事件的模式(EAP)和異步編程模型(APM)我上面不推薦的事件和回調(diào)。
2,并行編程并行編程的出現(xiàn)實際上是隨著CPU有多核而興起的,目的是充分利用多核CPU的計算能力。并行編程由于會提高CPU的利用率,更適合客戶端的一些應用,對于服務端的應用可能會造成負面影響(因為服務器本身就具有并行處理的特點,比如IIS會并行的處理多個請求)。我自己使用并行編程最多的場景是之前分析環(huán)境數(shù)據(jù)不確定度的時候,使用并行的方式計算蒙特卡洛模擬(計算上千次之后擬合),當然后來我使用泰勒級數(shù)展開來計算不確定度,沒有這么多的計算量就無需并行了。當然在計算多方案結(jié)果比較的情況下,還是繼續(xù)使用了并發(fā)計算。
在.NET中,并行的支持主要靠.NET 4.0引入的任務并行庫和并行LINQ。通過這些庫可以實現(xiàn)數(shù)據(jù)并行處理(處理方式相同,輸入數(shù)據(jù)不同,比如我上面提到的應用場景)或者任務并行處理(處理方式不同,且數(shù)據(jù)隔離)。通過使用并行處理庫,你不用關心Task的創(chuàng)建和管理(當然更不用說底層的線程了),只需要關注處理任務本身就行了。
具體的用法還是參考官方文檔:https://msdn.microsoft.com/en-us/library/dd460693(v=vs.110).aspx,當然《Parallel Programming with Microsoft .NET》這本書也行。
3,響應式編程響應式編程最近成為了一個Buzzword,其實微軟6年前就開始給.NET提供一個Reactive Extensions了。一開始要理解響應式編程有點困難,但是一旦理解了,你就會對它的強大功能愛不釋手。簡單來說,響應式編程把事件流看作數(shù)據(jù)流,不過數(shù)據(jù)流是從IEnumable中拉取的,而事件流是從IObservable推送給你的。為什么響應式編程可以實現(xiàn)并發(fā)呢?這是因為Rx做到線程不可知,每次事件觸發(fā),后續(xù)的處理會從線程池中任意取出一個線程來處理。且可以對事件設置窗口期和限流。舉個例子,你可以用Rx來讓搜索文本框進行延遲處理(而不用類似我很早的時候用個定時器來延遲了)。
要詳細了解Rx最好的方式就是瀏覽 IntroToRx.com 這個網(wǎng)站,當然還有官方文檔:。
4,數(shù)據(jù)流編程數(shù)據(jù)流(DataFlow)編程可能大家就更陌生了,不過還是有些常用場景可以使用數(shù)據(jù)流來解決。數(shù)據(jù)流其實是在任務并行庫(TPL)上衍生出來的一套處理數(shù)據(jù)的擴展(也結(jié)合了異步的特性),TPL也是處理并行編程中任務并行和數(shù)據(jù)并行的基礎庫。
望文生義,TPL DataFlow就是對數(shù)據(jù)進行一連串處理,首先為這樣的處理定義一套網(wǎng)格(mesh),網(wǎng)格中可以定義分叉(fork)、連接(join)、循環(huán)(loop)。數(shù)據(jù)流入這樣的處理網(wǎng)格就能夠并行的被處理。你可以認為網(wǎng)格是一種升級版的管道,實際上很多時候就是被當作管道來使用。使用場景可以是“分析文本文件中詞頻”,也可以是“處理生產(chǎn)者/消費者問題”。
參考資料當然也是官方文檔:https://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx。
5,Actor模型Scala有Akka,其實微軟研究院也推出了Orleans來支持了Actor模型的實現(xiàn),當然也有Akka.NET可用。Orleans設計的目標是為了方便程序員開發(fā)需要大規(guī)模擴展的云服務, 可用于實現(xiàn)DDD+EventSourcing/CQRS系統(tǒng)。
那么,我為什么喜歡使用C#來做并發(fā)編程呢?顯而易見,有上面這些唾手可得的工具,使用C#同樣可以輕易開發(fā)并發(fā)程序。
聯(lián)系客服