繁体   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