我為什么喜歡用C#來做並發編程


(此文章同時發表在本人微信公眾號“dotNET每日精華文章”,歡迎右邊二維碼來關注。)

題記:就語言和運行時層面,C#做並發編程一點都不弱,缺的是生態和社區。

硅谷才女朱贇(我的家門)昨天發了一篇文章《為什么用 Java —— 關於並發編程》,讓大家學習了Java中如何進行並發編程的一些基本知識。作為一個將近15年的.NET程序員,我覺得有必要給大家補充介紹一下C#進行並發編程的知識(當然不會太深入講解)。這篇文章無意進行技術比較,畢竟技術只是工具(大同小異,各有千秋),主要還是看用工具的人。

並發(英文Concurrency),其實是一個很泛的概念,字面意思就是“同時做多件事”,不過方式有所不同。在.NET的世界里面,並發一般涉及如下幾個方面:

  1. 多線程編程(已過時,不介紹)
  2. 異步編程
  3. 並行編程
  4. 響應式編程
  5. 數據流編程

為了支持以上編程,.NET提供了很多基礎功能,比如:委托,匿名函數,Lambda表達式,線程池,Task模型,支持並發的集合(線程安全集合和不可變集合) ,調度器,同步功能。在這里,就不對這些內容進行介紹了,大家可以自行搜索學習。另外,對於Actor模型,.NET中也有支持,但我不認為它屬於語言/運行時層面的並發,它更像架構層面的並發,我最后會簡單介紹。

1,異步編程

異步編程就是使用future模式(又稱promise)或者回調機制來實現(Non-blocking on waiting)。

如果使用回調或事件來實現(容易callback hell),不僅編寫這樣的代碼不直觀,很快就容易把代碼搞得一團糟。不過在.NET 4.5(C# 5)中引入的async/await關鍵字(在.NET 4.0中通過添加Microsoft.Bcl.Async包也可以使用),讓編寫異步代碼變得容易和優雅。通過使用async/await關鍵字,可以像寫同步代碼那樣編寫異步代碼,所有的回調和事件處理都交給編譯器和運行時幫你處理了。

使用異步編程有兩個好處:不阻塞主線程(比如UI線程),提高服務端應用的吞吐量。所以微軟推薦ASP.NET中默認使用異步來處理請求。

要詳細了解異步編程,可以參考官方文檔:https://msdn.microsoft.com/en-us/library/jj152938(v=vs.110).aspx和《Async in C# 5.0》這本書。另外,在這個官方文檔中,微軟還特意把異步編程分作了3種不同的模型:基於任務的模式(TAP)就是我上面推薦的這種,基於事件的模式(EAP)和異步編程模型(APM)我上面不推薦的事件和回調。

2,並行編程

並行編程的出現實際上是隨着CPU有多核而興起的,目的是充分利用多核CPU的計算能力。並行編程由於會提高CPU的利用率,更適合客戶端的一些應用,對於服務端的應用可能會造成負面影響(因為服務器本身就具有並行處理的特點,比如IIS會並行的處理多個請求)。我自己使用並行編程最多的場景是之前分析環境數據不確定度的時候,使用並行的方式計算蒙特卡洛模擬(計算上千次之后擬合),當然后來我使用泰勒級數展開來計算不確定度,沒有這么多的計算量就無需並行了。當然在計算多方案結果比較的情況下,還是繼續使用了並發計算。

在.NET中,並行的支持主要靠.NET 4.0引入的任務並行庫和並行LINQ。通過這些庫可以實現數據並行處理(處理方式相同,輸入數據不同,比如我上面提到的應用場景)或者任務並行處理(處理方式不同,且數據隔離)。通過使用並行處理庫,你不用關心Task的創建和管理(當然更不用說底層的線程了),只需要關注處理任務本身就行了。

具體的用法還是參考官方文檔:https://msdn.microsoft.com/en-us/library/dd460693(v=vs.110).aspx,當然《Parallel Programming with Microsoft .NET》這本書也行。

3,響應式編程

響應式編程最近成為了一個Buzzword,其實微軟6年前就開始給.NET提供一個Reactive Extensions了。一開始要理解響應式編程有點困難,但是一旦理解了,你就會對它的強大功能愛不釋手。簡單來說,響應式編程把事件流看作數據流,不過數據流是從IEnumable中拉取的,而事件流是從IObservable推送給你的。為什么響應式編程可以實現並發呢?這是因為Rx做到線程不可知,每次事件觸發,后續的處理會從線程池中任意取出一個線程來處理。且可以對事件設置窗口期和限流。舉個例子,你可以用Rx來讓搜索文本框進行延遲處理(而不用類似我很早的時候用個定時器來延遲了)。

要詳細了解Rx最好的方式就是瀏覽 IntroToRx.com 這個網站,當然還有官方文檔:https://msdn.microsoft.com/en-us/data/gg577609

4,數據流編程

數據流(DataFlow)編程可能大家就更陌生了,不過還是有些常用場景可以使用數據流來解決。數據流其實是在任務並行庫(TPL)上衍生出來的一套處理數據的擴展(也結合了異步的特性),TPL也是處理並行編程中任務並行和數據並行的基礎庫。

望文生義,TPL DataFlow就是對數據進行一連串處理,首先為這樣的處理定義一套網格(mesh),網格中可以定義分叉(fork)、連接(join)、循環(loop)。數據流入這樣的處理網格就能夠並行的被處理。你可以認為網格是一種升級版的管道,實際上很多時候就是被當作管道來使用。使用場景可以是“分析文本文件中詞頻”,也可以是“處理生產者/消費者問題”。

參考資料當然也是官方文檔:https://msdn.microsoft.com/en-us/library/hh228603(v=vs.110).aspx

5,Actor模型

Scala有Akka,其實微軟研究院也推出了Orleans來支持了Actor模型的實現,當然也有Akka.NET可用。Orleans設計的目標是為了方便程序員開發需要大規模擴展的雲服務, 可用於實現DDD+EventSourcing/CQRS系統。

官方網站是:http://dotnet.github.io/orleans/,善友也有介紹:http://www.cnblogs.com/shanyou/p/4295523.html

那么,我為什么喜歡使用C#來做並發編程呢?顯而易見,有上面這些唾手可得的工具,使用C#同樣可以輕易開發並發程序。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM