3

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

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

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

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

无论如何我可以把它变成一个更精简的版本吗?或者这只是普通字典所能做的最好的吗?

4

5 回答 5

2

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

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

于 2013-02-26T17:03:47.640 回答
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;
}
于 2013-02-26T17:13:09.727 回答
0

这是最好的。

锁定是必需的,因为字典不保证您可以并行更新和阅读。由于内部数据结构的更改,即使单个调用以使元素与其他线程上的更新同时运行也可能会失败。

请注意,该行为已在Dictionary的 Thread Safety 部分中明确介绍

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

于 2013-02-26T17:04:51.583 回答
0

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

于 2013-02-26T17:07:11.743 回答
0

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

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

这可确保您仅在实际需要时才锁定,但也确保一旦锁定,您仍需要添加值。性能应该比总是锁定要好。但不要相信我的话。运行测试!

于 2013-02-26T17:27:04.540 回答