.NET並發編程-數據結構不可變性
本系列學習在.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!
下集:數據並行
寫給普通:
習慣了是個很強大的詞
它可以代替所有的一言難盡