簡體   English   中英

如何創建Lockfree集合集合

[英]How to create a Lockfree collection of collection

我需要創建一個集合集合。 多個線程調用該集合以添加項和查找項。 添加后,項目將不會被刪除。 目前,在添加元素時我需要鎖定整個集合。 有沒有辦法讓它無鎖。 或者,我可以使用更好的數據結構或模式嗎? 這是我的代碼的簡化版本:

readonly ConcurrentDictionary<string, ConcurrentDictionary<int, int>> dict = new ConcurrentDictionary<string, ConcurrentDictionary<int, int>>();

void AddUpdateItem(string s, int k, int v)
{
    ConcurrentDictionary<int, int> subDict;
    if (dict.TryGetValue(s, out subDict))
    {
        subDict[k] = v;
    }
    else
    {
        lock (dict)
        {
            if (dict.TryGetValue(s, out subDict))
            {
                subDict[k] = v;
            }
            else
            {
                subDict = new ConcurrentDictionary<int, int>();
                subDict[k] = v;
                dict[s] = subDict;
            }
        }
    }
}

您可以通過使用不變性來使哈希表無鎖,但如果存在爭用則不太可能有效。 基本上,您需要一個可以原子交換的字典內容類。 您構建了當前內容的副本,並進行了一次更改,然后使用比較和交換原語將其與現有版本進行交換。 如果比較和交換失敗,請從復制步驟開始。

您可以原子地交換一個哈希桶,這會使爭用更不常見,並重試更便宜。 ConcurrentDictionary已經使用了這種優化,以減少鎖爭用)但是增加桶的數量仍然需要上面概述的方法。

看看Eric Lippert的博客,其中涵蓋了不可變數據結構。 他有一個很好的二叉樹示例 ,它應該向您展示制作無鎖散列表所需的技術。

方法ConcurrentDictionary.GetOrAdd是線程安全的(雖然不是原子的)。 它保證返回的對象對於所有線程都是相同的。 您的代碼可以重寫為:

void AddUpdateItem(string s, int k, int v)
{
    var subDict = dict.GetOrAdd(s, _ => new ConcurrentDictionary<int, int>());
    subDict[k] = v;
}

您是否在代碼中使用任務或線程? 無論如何, ConcurrentDictionary被設計為線程安全的。 添加或刪除元素時不需要使用鎖。 從MSDN鏈接如何:在ConcurrentDictionary中添加和刪除項目解釋了如何使用它。

如果您推測性地創建子字典,則有一個更簡單的解決方案:

readonly ConcurrentDictionary<string, ConcurrentDictionary<int, int>> dict = new ConcurrentDictionary<string, ConcurrentDictionary<int, int>>();

void AddUpdateItem( string s, int k, int v )
{
    ConcurrentDictionary<int, int> subDict;

    while ( true )
    {
        if ( dict.TryGetValue( s, out subDict ) )
        {
            subDict[ k ] = v;
            break;
        }

        // speculatively create new sub-dictionary
        subDict = new ConcurrentDictionary<int, int>();
        subDict[ k ] = v;

        // this can only succeed for one thread
        if ( dict.TryAdd( s, subDict ) ) break;
    }
}

在你實現無鎖集合之前,先看看ReadWriteLock來解決你的問題。 如果沒有(例如因為你有很大的寫入爭用),那么實際上並沒有一種通用的方法。

我過去使用的一種技術是使用一個線程專用線程來管理集合,並使用Interlocked.Exchange將新對象封送到該線程和一個不可變集合。 使用這種方法,您的編寫器線程將在一個單獨的列表中進行管理,無論何時創建或銷毀編寫器,您都需要鎖定它,因此只有在這是一個罕見的事件時才有效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM