當前位置: 妍妍網 > 碼農

C#中的分區鎖和Concurrent集合差異

2024-06-22碼農

在C#中,對於多執行緒環境下的集合操作,使用 Concurrent 集合(如 ConcurrentDictionary<TKey, TValue> )通常比使用手動 lock 來實作分區集合的效率更高。下面是詳細的解釋和比較。

分區集合(Partitioned Collections)L ock

分區集合是一種將集合分割成多個部份,每個部份有自己的鎖,這樣可以減少鎖的競爭,提高並行效能。實作分區集合需要手動管理多個鎖和分區邏輯,這通常會增加程式碼復雜度。

範例:使用分區和 lock 實作執行緒安全集合

public classPartitionedDictionary<TKey, TValue>{private readonly int _partitionsCount;private readonly object[] _locks;private readonly Dictionary<TKey, TValue>[] _partitions;publicPartitionedDictionary(int partitionsCount){ _partitionsCount = partitionsCount; _locks = new object[partitionsCount]; _partitions = new Dictionary<TKey, TValue>[partitionsCount];for (int i = 0; i < partitionsCount; i++) { _locks[i] = new object(); _partitions[i] = new Dictionary<TKey, TValue>(); } }privateintGetPartitionIndex(TKey key){return (key.GetHashCode() & int.MaxValue) % _partitionsCount; }publicvoidAddOrUpdate(TKey key, TValue value){int index = GetPartitionIndex(key); lock (_locks[index]) { _partitions[index][key] = value; } }publicboolTryGetValue(TKey key, out TValue value){int index = GetPartitionIndex(key); lock (_locks[index]) {return _partitions[index].TryGetValue(key, out value); } }}

Concurrent 集合

Concurrent 集合,如 ConcurrentDictionary<TKey, TValue> ,是由 .NET 提供的內建執行緒安全集合。它們使用了細粒度鎖和無鎖演算法來實作高效的並行操作。這些集合經過高度最佳化,能在高並行環境中提供良好的效能。

範例:使用 ConcurrentDictionary

ConcurrentDictionary<int, string> dictionary = new ConcurrentDictionary<int, string>();// 添加或更新元素dictionary.AddOrUpdate(1, "value1", (key, oldValue) => "newValue");// 讀取元素if (dictionary.TryGetValue(1, outstringvalue)){ Console.WriteLine(value);}

比較

  1. 效能

  • Concurrent 集合 :通常在高並行場景下效能更好,因為它們使用了更細粒度的鎖或無鎖演算法,減少了鎖的競爭。

  • 分區集合 + lock :雖然分區可以減少鎖競爭,但實作上通常不如 Concurrent 集合最佳化得好,尤其在極高並行下,效能提升有限。

  • 程式碼復雜度

  • Concurrent 集合 :內建實作,使用簡單,程式碼更簡潔,減少了手動管理鎖和分區的復雜性。

  • 分區集合 + lock :需要手動實作分區邏輯和管理多個鎖,增加了程式碼復雜性和出錯的可能性。

  • 靈活性和適用場景

  • Concurrent 集合 :適合大多數多執行緒讀寫的場景,特別是當集合操作比較頻繁時。

  • 分區集合 + lock :適用於某些特定場景,比如當集合大小固定,或者某些操作對鎖的競爭非常敏感時,但這種情況較少。

  • 結論

    在大多數情況下,使用 Concurrent 集合(如 ConcurrentDictionary<TKey, TValue> )比手動實作分區集合並使用 lock 更高效且簡潔。 Concurrent 集合經過最佳化,能夠提供高效能的並行操作,同時減少了手動管理鎖和分區的復雜性。因此,建議優先考慮使用 Concurrent 集合,只有在非常特殊的需求下,才考慮使用分區集合加 lock 的方式。

    ArrayPool使用分區鎖的原因

    ArrayPool<T> 是 .NET 提供的用於管理陣列租借和歸還的記憶體池。它使用分區鎖(分段鎖)而不是 Concurrent 集合,主要原因包括效能最佳化和資源管理的需求。以下是具體原因和背景。

    1. 效能最佳化

    細粒度鎖和無鎖操作

  • ArrayPool<T> 設計為高效能記憶體池,適用於頻繁的租借和歸還操作。它透過分區鎖實作細粒度釘選,減少了鎖競爭,提升了並行效能。

  • Concurrent 集合雖然提供了執行緒安全性,但在某些高效能要求的場景下,它的額外開銷可能不如直接使用分區鎖高效。例如, ConcurrentDictionary 在操作時可能涉及多個內部鎖和復雜的協調機制,這會引入額外的效能開銷。

  • 低延遲需求

  • ArrayPool<T> 的設計目標是提供低延遲的陣列租借和歸還。透過使用分區鎖, ArrayPool<T> 可以在大多數情況下避免全域鎖的競爭,從而降低操作延遲。

  • 2. 精細控制資源管理

    自訂邏輯和最佳化

  • ArrayPool<T> 使用分區鎖可以對每個分區套用特定的最佳化和邏輯,這些最佳化可能無法透過通用的 Concurrent 集合實作。例如, ArrayPool<T> 可以根據特定的分區進行客製化的陣列管理策略,提高資源利用率和回收效率。

  • 分區鎖的實作方式可以針對陣列池的特定需求進行最佳化,如分區的選擇演算法、陣列的管理策略等。這些最佳化是根據 ArrayPool<T> 的使用模式量身客製的。

  • 更好的記憶體管理

  • ArrayPool<T> 需要在管理記憶體時有更多的控制權。透過分區鎖,可以在每個分區內實作特定的記憶體管理策略,確保在高並行情況下仍然能高效地管理和復用記憶體。

  • 分區鎖允許更精細的記憶體管理,如對不同大小的陣列進行不同的處理,這在 Concurrent 集合中是無法直接實作的。

  • 3. 專用場景的效能優勢

    高並行租借和歸還

  • ArrayPool<T> 的使用場景通常涉及大量高並行的陣列租借和歸還操作。分區鎖可以更好地適應這種高並行需求,透過減少全域釘選,提高整體吞吐量。

  • 使用分區鎖能夠更有效地管理大量的並行請求,將鎖競爭局限在特定的分區內,從而避免全域鎖帶來的效能瓶頸。

  • 範例程式碼

    以下是一個簡化的範例,展示 ArrayPool<T> 如何使用分區鎖來管理陣列池:

    public classSimpleArrayPool<T>{private readonly object[] _locks;private readonly List<T[]>[] _arrays;private readonly int _partitions;publicSimpleArrayPool(int partitions){ _partitions = partitions; _locks = new object[partitions]; _arrays = new List<T[]>[partitions];for (int i = 0; i < partitions; i++) { _locks[i] = new object(); _arrays[i] = new List<T[]>(); } }privateintGetPartitionIndex(int length){return (length.GetHashCode() & int.MaxValue) % _partitions; }public T[] Rent(int length) {int index = GetPartitionIndex(length); lock (_locks[index]) {if (_arrays[index].Count > 0) { var array = _arrays[index][_arrays[index].Count - 1]; _arrays[index].RemoveAt(_arrays[index].Count - 1);returnarray; } }returnnew T[length]; }publicvoidReturn(T[] array){int index = GetPartitionIndex(array.Length); lock (_locks[index]) { _arrays[index].Add(array); } }}

    總結

    ArrayPool<T> 選擇使用分區鎖而不是 Concurrent 集合,主要是出於效能最佳化和資源管理的需求。分區鎖可以提供更高的並行效能和更細粒度的控制,滿足 ArrayPool<T> 的高效能和低延遲需求。透過客製化的最佳化和管理策略,分區鎖在陣列池的使用場景中表現得更加高效。

    BeetleX

    開源跨平台通訊框架(支持TLS)

    提供HTTP,Websocket,MQTT,Redis,RPC和服務閘道器開源元件

    個人微信:henryfan128 QQ:28304340
    有豐富的高吐網路服務設計經驗

    關註公眾號

    https://github.com/beetlex-io/