我有一个要安全更新的静态词典。最初,字典是空的,但在应用程序的生命周期中,它会添加新的值。此外,整数值将充当可以递增和递减的单独计数器。
private static Dictionary<string, int> foo = new Dictionary<string, int>();
public static void Add(string bar)
{
if (!foo.ContainsKey(bar))
foo.Add(bar, 0);
foo[bar] = foo[bar] + 1;
}
public static void Remove(string bar)
{
if (foo.ContainsKey(bar))
{
if (foo[bar] > 0)
foo[bar] = foo[bar] - 1;
}
}
我一直在阅读 Interlocked class作为提供线程安全的一种方式,看起来这可能是我可以使用的东西:
public static void Add(string bar)
{
if (!foo.ContainsKey(bar))
foo.Add(bar, 0);
Interlocked.Increment(ref foo[bar]);
}
public static void Remove(string bar)
{
if (foo.ContainsKey(bar))
{
if (foo[bar] > 0)
Interlocked.Decrement(ref foo[bar]);
}
}
但是,我的直觉是,当我实际向字典中添加新项目时,这不会解决问题,因此立即想到锁定:
private static Dictionary<string, int> foo = new Dictionary<string, int>();
private static object myLock = new object();
public static void Add(string bar)
{
lock(myLock)
{
if (!foo.ContainsKey(bar))
foo.Add(bar, 0);
Interlocked.Increment(ref foo[bar]);
}
}
public static void Remove(string bar)
{
lock(myLock)
{
if (foo.ContainsKey(bar))
{
if (foo[bar] > 0)
Interlocked.Decrement(ref foo[bar]);
}
}
}
这种方法是否理想,甚至是正确的?可以改进吗?我离题了吗?
最佳答案
lock
是好的(而且是必要的;你怀疑 Interlocked
是不够的是对的)但是一旦你这样做了 Interlocked.Increment
和 Interlocked.Decrement
是不必要的。访问 Dictionary<TKey, TValue>
的问题来自多个线程的是一个线程可以触发内部哈希表的重建,然后该线程在重建过程中被换出另一个线程,现在出现并添加到字典中,对字典的内部结构造成严重破坏。
此外,您的实现很好,因为您 lock
在 private
上对象而不是 this
.作为chibacity指出,这lock
对象应该是 readonly
虽然。
请注意不要自欺欺人地认为您现在已经使字典在多线程场景中无懈可击。例如,可能会发生以下情况:
Thread 1 looks up string
"Hello, World!"
in the dictionary, receives back the count1
.Thread 1 is swapped out for Thread 2.
Thread 2 calls remove with key
"Hello, World!"
setting the count to0
.Thread 2 is swapped out for Thread 1.
Thread 1 now thinks that the count is
1
but it is actually0
.
最后,在 .NET 4.0 中,您应该考虑使用 ConcurrentDictionary<TKey, TValue>
.请注意,这有效地消除了 lock
的需要。在字典上调用实例方法时,但它并没有消除上述情况的发生。
关于c# - .NET4.0 : Thread-Safe Manner of Updating A Dictionary and Its Values,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4234049/