.NET併發編程-數據結構不可變性

本系列學習在.NET中的併發並行編程模式,實戰技巧

內容目錄

.NET不可變集合.NET併發集合函數式數據結構設計一個不可變類

作爲程序員經常遇到產品上線後出現各種莫名其妙的問題,在我本地是好好的啊,也成爲程序員面對未知問題的第一反應。這種不容易復現的問題,無非就是硬件不一致和軟件不一致,更多的問題出在軟件環境上,用戶量、 併發這種測試容易遺漏的點。

爲了保證編寫的代碼在不同的環境中出現一致的行爲結果,通常就要利用不可變的數據結構。數據一旦創建後就不能修改其本身,修改後會產生新的數據。

.NET不可變集合

在.NET4.5引入不可變集合,在命名空間System.Collecttions.Immutable中。(注意這個類庫不是.net核心類庫,需要從nuget上安裝)。不可變的集合結構每次修改數據後都會生成新的集合。像String類型一樣,對它Substring,Replace都會生成新的字符串。

//可以將普通可變集合直接轉爲不可變集合
var dic = new Dictionary<intint>().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!
下集:數據並行

 

 

寫給普通:

習慣了是個很強大的詞

它可以代替所有的一言難盡

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章