ConcurrentDictionary Pitfall - 来自 GetOrAdd 和 AddOrUpdate 的委托

本文关键字:AddOrUpdate GetOrAdd Pitfall 来自 ConcurrentDictionary | 更新日期: 2023-09-27 18:33:21

ConcurrentDictionary的文档没有明确说明,所以我想我们不能期望委托valueFactoryupdateValueFactory同步它们的执行(分别来自 GetOrAdd(( 和 AddOrUpdate(( 操作(。

因此,我认为如果不手动实现我们自己的并发控制,也许只是在委托上使用[MethodImpl(MethodImplOptions.Synchronized)],我们就无法实现其中需要并发控制的资源的使用。

我说的对吗?或者ConcurrentDictionary线程安全的事实,我们可以期望对这些委托的调用会自动同步(也是线程安全的(?

ConcurrentDictionary Pitfall - 来自 GetOrAdd 和 AddOrUpdate 的委托

是的,你是对的,用户代表没有通过ConcurrentDictionary同步。 如果您需要这些同步,这是您的责任。

MSDN 本身说:

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

请参阅"如何:在 ConcurrentDictionary 中添加和删除项

这是因为ConcurrentDictionary不知道您提供的委托将执行什么操作或其性能,因此如果它试图锁定它们,它可能会真正对性能产生负面影响并破坏 ConcurrentDictionary 的值。

因此,如有必要,用户有责任同步其委托。 上面的 MSDN 链接实际上有一个很好的示例,说明它所做的保证和不做的保证。

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

例如,AddOrUpdate算法如下所示。

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

请注意两件事。

  • 无需同步委托的执行。
  • 委托可能会被多次执行,因为它们是在循环调用的。

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

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