本系列學習在.NET中的併發並行程式設計模式,實戰技巧
內容目錄
.NET不可變集合.NET併發集合函式式資料結構設計一個不可變類
作為程式設計師經常遇到產品上線後出現各種莫名其妙的問題,在我本地是好好的啊,也成為程式設計師面對未知問題的第一反應。這種不容易復現的問題,無非就是硬體不一致和軟體不一致,更多的問題出在軟體環境上,使用者量、 併發這種測試容易遺漏的點。
為了保證編寫的程式碼在不同的環境中出現一致的行為結果,通常就要利用不可變的資料結構。資料一旦建立後就不能修改其本身,修改後會產生新的資料。
.NET不可變集合
在.NET4.5引入不可變集合,在名稱空間System.Collecttions.Immutable中。(注意這個類庫不是.net核心類庫,需要從nuget上安裝)。不可變的集合結構每次修改資料後都會生成新的集合。像String型別一樣,對它Substring,Replace都會生成新的字串。
//可以將普通可變集合直接轉為不可變集合
var dic = new Dictionary<int, int>().ToImmutableDictionary();
//直接建立不可變集合
var list = ImmutableList.Create<int>();
list = list.Add(1);
list = list.Add(2);
list = list.Add(3);
由於集合不可變,也就保證了多執行緒的安全。直接將集合丟給每個執行緒,原始集合不會變化。
.NET併發集合
還有一種執行緒安全集合在System.Collections.Concurrent中,在多執行緒環境中建議使用此類集合。Concurrent集合是可變集合,但提供了細粒度和無鎖模式來提高多執行緒應用程式的效能和可擴充套件性。像ConcurrentDictionary字典,除了像傳統字典Dictionary使用,還提供了很多相容併發的方法,如AddOrUpdate或GetOrAdd等。如果不使用併發集合,在多執行緒環境中我們需要設定鎖來保證資料的一致性。
函式式資料結構
可持久化資料結構也稱之為函式式資料結構。可持久化意味著資料結構是不可變的,修改只會返回修改後的新資料結構。(這裡資料持久化和IO持久化區分)。大多數命令式資料結構都是短暫的,修改就破壞其結構。如Dictionary,List,Queue等。
不可變性可能會帶來一定的損耗,每次修改都會生成新的資料資料結構。但在託管程式語言中,如C# 和Java中,已經做了足夠多的優化,且在多核時代,基本可以忽略效能的影響。
以連結串列資料結構為例說明託管語言在共享資料結構上做的優化
不可變的資料集合,每次修改後,不是完整拷貝原集合,比如集合中追加一項,只會修改引用指向的位置,共享剩餘其他結構。
設計一個不可變類
C#有readonly和const兩個關鍵字,還記得他們的區別和用處嗎。const靜態常量,編譯時被解析,通過類訪問。readonly動態常量,可延遲到建構函式中初始化,通過類例項訪問。
public class Person
{
public const string Contry = "中國";
public string Name { get; }
public readonly Address Address;
public Person(string name, Address address)
{
this.Name = name;
this.Address = address;
}
}
public class Address
{
public string Street;
public Address(string street)
{
this.Street = street;
}
}
程式碼示例中,控制了Person的Address地址是不能被修改的,但它的底層欄位Street仍然可以被修改。這就會導致person.Address.Street="M78星雲"這樣的行為,所以這就是淺不可變。微軟考慮到不可變程式設計的重要性,隨後又在C#6.0又引入了自動屬性的概念,可以輕鬆的建立一個不可變類。像示例中的public string Name { get; }這樣。
to be contiued!
下集:資料並行
寫給普通:
習慣了是個很強大的詞
它可以代替所有的一言難盡