[英]Double checked locking on Dictionary “ContainsKey”
我的团队目前正在讨论这个问题。
有问题的代码就是这样的
if (!myDictionary.ContainsKey(key))
{
lock (_SyncObject)
{
if (!myDictionary.ContainsKey(key))
{
myDictionary.Add(key,value);
}
}
}
我见过的一些帖子说这可能是一个很大的NO NO(当使用TryGetValue时)。 然而,我们团队的成员说没关系,因为“ContainsKey”不会对密钥集合进行迭代,而是通过O(1)中的哈希代码检查密钥是否包含在内。 因此他们声称这里没有危险。
我想就此问题得到您的诚实意见。
不要这样做。 这不安全。
您可以从一个线程调用ContainsKey
,而另一个线程调用Add
。 这完全不受Dictionary<TKey, TValue>
。 如果Add
需要重新分配存储桶等,我可以想象你可能得到一些非常奇怪的结果或异常。 它可能是以你没有看到任何令人讨厌的效果的方式编写的,但我不想依赖它。
它是利用一件事双重检查锁定为简单的读/写操作的领域,虽然我仍然反对-这是另一个作出已明确说明, 不被用于多个并发呼叫安全的API调用。
如果您使用的是.NET 4,那么ConcurrentDictionary
可能就是前进之路。 否则,只需锁定每次访问。
如果您处于多线程环境中,您可能更愿意使用ConcurrentDictionary。 几个月前我在博客上发表了这篇文章,你可能会发现这篇文章很有用: http : //colinmackay.co.uk/blog/2011/03/24/parallelisation-in-net-4-0-the-concurrent-dictionary /
此代码不正确。 Dictionary<TKey, TValue>
类型不支持同时读写操作。 即使在锁内调用了Add
方法, ContainsKey
也不会。 因此,它很容易导致违反同时读/写规则,并会导致实例中的损坏
它看起来不是线程安全的,但可能很难让它失败。
迭代与散列查找参数不成立,例如可能存在哈希冲突。
如果这个字典很少被编写并且经常被读取,那么我经常通过在写入时替换整个字典来使用安全双锁。 如果您可以批量写入以减少它们的频率,则此功能尤其有效。
例如,这是我们使用的方法的简化版本,它试图获取与类型相关联的模式对象,如果不能,则继续并为其在同一类型中找到的所有类型创建模式对象。程序集作为指定的类型,以最小化整个字典必须复制的次数:
public static Schema GetSchema(Type type)
{
if (_schemaLookup.TryGetValue(type, out Schema schema))
return schema;
lock (_syncRoot) {
if (_schemaLookup.TryGetValue(type, out schema))
return schema;
var newLookup = new Dictionary<Type, Schema>(_schemaLookup);
foreach (var t in type.Assembly.GetTypes()) {
var newSchema = new Schema(t);
newLookup.Add(t, newSchema);
}
_schemaLookup = newLookup;
return _schemaLookup[type];
}
}
因此,在这种情况下,字典最多将被重建,因为有类型需要模式的程序集。 对于应用程序生命周期的其余部分,字典访问将是无锁的。 字典副本成为程序集的一次性初始化成本。 字典交换是线程安全的,因为指针写入是原子的,因此整个引用立即被切换。
您也可以在其他情况下应用类似的原则。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.