从字典中读取值的线程安全方式,可能存在也可能不存在

时间:2013-02-26 17:01:44

标签: c# dictionary thread-safety locking

所以,我被ConcurrentDictionary宠坏了,它是一个很棒的TryGetValue方法。但是,我仅限于使用常规词典,因为这是针对手机和其他平台的便携式类库。我正在尝试编写一个非常有限的Dictionary子集并以线程安全的方式公开它。

我基本上需要来自ConcurrentDictionary的GetOrAdd之类的东西。现在,我的实现方式如下:

        lock (lockdictionary)
        {
            if (!dictionary.ContainsKey(name))
            {
                value = new foo();
                dictionary[name] = value;
            }
            value = dictionary[name];
        }

这基本上和我能得到的一样好吗?我认为如果密钥不存在并且它被添加,则锁定只需真正但是,如果存在则没有好的“获取值,否则返回null”方法。如果我要省略ContainsKey位,当密钥不存在时,我会得到一个例外,因为密钥不存在。

无论如何,我可以将它变成更精益的版本吗?或者这只是普通字典能做的最好的事情吗?

5 个答案:

答案 0 :(得分:2)

在并发写入程序存在的情况下,即使是读取也需要锁定。所以,是的,如果你改变字典,这就好了。

当然,每次写入内容时,您都可以创建整个字典的副本。这样读者可能会看到过时的版本,但他们可以安全地阅读。

答案 1 :(得分:1)

您可以尝试使用ReaderWriterLockSlim。例如:

ReaderWriterLockSlim locker = new ReaderWriterLockSlim();

//..

public string GetOrAdd(string name)
{
    locker.EnterUpgradeableReadLock();
    try
    {
        if(!dictionary.ContainsKey(name))
        {
            locker.EnterWriteLock();
            try
            {
                dictionary[name] = new foo();
            }
            finally
            {
            locker.ExitWriteLock();
            }
        }
        value = dictionary[name];
    }
    finally
    {
        locker.ExitUpgradeableReadLock();
    }
    return value;
}

答案 2 :(得分:0)

这是最好的。

需要锁定,因为字典无法保证您可以并行更新和读取。即使单个调用get元素与其他线程上的更新同时运行也可能因内部数据结构的更改而失败。

请注意,Dictionary

的“线程安全”部分明确介绍了该行为
  

只要不修改集合,词典就可以同时支持多个阅读器。即便如此,通过集合枚举本质上不是一个线程安全的过程。在枚举与写访问争用的极少数情况下,必须在整个枚举期间锁定该集合。要允许多个线程访问集合以进行读写,您必须实现自己的同步。

答案 3 :(得分:0)

你的实施很好。请注意,在无竞争访问的情况下,锁实现具有可忽略的性能损失。但是,为了实现真正的线程安全,你必须使用带有字典的每个操作的锁 - 我建议编写包装类,如SynchronizedDictinory以将同步逻辑保存在一个地方

答案 4 :(得分:0)

您可以使用双重检查模式,如下所示:

if (!dictionary.ContainsKey(name))
{
    lock (lockdictionary)
    {
        if (!dictionary.ContainsKey(name))
        {
            value = new foo();
            dictionary[name] = value;
        }
        value = dictionary[name];
    }
}

这确保您只在实际需要时才锁定,但也确保一旦您锁定,您仍然需要添加该值。性能应该优于始终锁定。但是不要相信我的话。运行测试!