1. 程式人生 > >C#程式設計高併發的幾種處理方法

C#程式設計高併發的幾種處理方法

併發(英文Concurrency),其實是一個很泛的概念,字面意思就是“同時做多件事”,不過方式有所不同。在.NET的世界裡面,處理高併發大致有以下幾種方法:

1,非同步程式設計

非同步程式設計就是使用future模式(又稱promise)或者回調機制來實現(Non-blocking on waiting)。如果使用回撥或事件來實現(容易callback hell),不僅編寫這樣的程式碼不直觀,很快就容易把程式碼搞得一團糟。

不過在.NET 4.5 及以上框架中引入的async/await關鍵字(在.NET 4.0中通過新增Microsoft.Bcl.Async包也可以使用),讓編寫非同步程式碼變得容易和優雅。通過使用async/await關鍵字,可以像寫同步程式碼那樣編寫非同步程式碼,所有的回撥和事件處理都交給編譯器和執行時幫你處理了,簡單好用。

使用非同步程式設計有兩個好處:不阻塞主執行緒(比如UI執行緒),提高服務端應用的吞吐量。所以微軟推薦ASP.NET中預設使用非同步來處理請求。

例如:我用非同步做微信模板訊息推送。

/// <summary>
/// 使用非同步Action測試非同步模板訊息介面
/// </summary>
/// <param name="checkcode"></param>
/// <returns></returns>
public async Task<string> TemplateMessageAsync(string openId, string first, string keyword1, string keyword2, string keyword3, string keyword4, string remark, string url)
{
    if (openId == null)
    {
        return ReturnString(7771, "OPENID不能為空");
    }
    else
    {
        var testData = new //TestTemplateData()
        {
            first = new TemplateDataItem(first),
            keyword1 = new TemplateDataItem(keyword1),
            keyword2 = new TemplateDataItem(keyword2),
            keyword3 = new TemplateDataItem(keyword3),
            keyword4 = new TemplateDataItem(keyword4),
            remark = new TemplateDataItem(remark)
        };

        var result = await TemplateApi.SendTemplateMessageAsync(_wechat.APPID, openId, "m6td4jp_heMA5rhopbUaHApOlp2DD5x18BMXWKj3M5U", url, testData);
        return ReturnString(0, "成功");
    }
}

 2,並行程式設計

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

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

3,響應式程式設計

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

4,資料流程式設計

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

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

5,Actor模型

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