ConcurrentDictionary陷阱 - 来自GetOrAdd和AddOrUpdate的委托工厂是否同步?

时间:2012-05-07 17:43:05

标签: c# .net multithreading concurrency thread-safety

ConcurrentDictionary的文档没有明确的状态,所以我想我们不能指望代理 valueFactory updateValueFactory 让它们的执行同步(分别来自GetOrAdd()和AddOrUpdate()操作)。

所以,我认为我们无法在其中实现需要并发控制的资源,而无需手动实现我们自己的并发控制,可能只是在代理上使用[MethodImpl(MethodImplOptions.Synchronized)]

我是对的吗?或者ConcurrentDictionary是线程安全的事实我们可以预期对这些代理的调用会自动同步(线程安全)吗?

2 个答案:

答案 0 :(得分:34)

是的,您是对的,ConcurrentDictionary未同步用户代理。如果您需要那些同步,这是您的责任。

MSDN本身说:

  

此外,尽管ConcurrentDictionary的所有方法都是   线程安全,并非所有方法都是原子的,特别是GetOrAdd和   AddOrUpdate。传递给这些方法的用户委托是   在字典的内部锁外部调用。 (这样做是为了   防止未知代码阻塞所有线程。)

See "How to: Add and Remove Items from a ConcurrentDictionary

这是因为ConcurrentDictionary不知道你提供的代理会做什么或者它的性能,所以如果它试图锁定它们,它可能会对性能产生负面影响并破坏ConcurrentDictionary的值。

因此,如果有必要,用户有责任同步他们的代理。上面的MSDN链接实际上有一个很好的例子,说明它做了什么,不做什么。

答案 1 :(得分:25)

这些代表不仅没有同步,而且甚至不能保证只发生一次。事实上,每次调用AddOrUpdate时,它们都可以执行多次。

例如,AddOrUpdate的算法看起来像这样。

TValue value;
do
{
  if (!TryGetValue(...))
  {
    value = addValueFactory(key);
    if (!TryAddInternal(...))
    {
      continue;
    }
    return value;
  }
  value = updateValueFactory(key);
} 
while (!TryUpdate(...))
return value;

请注意这里的两件事。

  • 无需同步代理的执行。
  • 委托人可能会被多次执行,因为他们在循环中被调用

所以你需要确保做两件事。

  • 为代表提供您自己的同步。
  • 确保您的代表没有任何副作用,这些副作用取决于执行次数。