【问题标题】:ConcurrentDictionary Pitfall - Are delegates factories from GetOrAdd and AddOrUpdate synchronized?ConcurrentDictionary Pitfall - GetOrAdd 和 AddOrUpdate 的委托工厂是否同步?
【发布时间】:2012-05-16 05:26:01
【问题描述】:

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

    MSDN 本身说:

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

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

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

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

    【讨论】:

    • 所以我将 GetOrAdd 方法调用包装在一个锁中,但这使得 ConcurrentDictionary 的目的毫无用处。有没有最好的办法?
    • 我对此有点困惑,我们基本上是说如果将委托作为键传递,那么它的执行在评估时将不会同步?或者如果我们有一个类型为“delegate”的字典作为值......当我们尝试执行委托(即dictionary[key](argument);)时,执行将不会同步?或者,我是否完全错过了这一点?谢谢。
    • @Snoopy:评估要添加的值的委托未同步。这意味着,如果您的委托做了一些不是线程安全的事情并且您没有尝试自己同步该操作,那么就会发生不好的事情。但是,作为值工厂的委托通常不会做本质上不是线程安全的事情。大多数时候委托可能只是在做new SomeObject(),这绝对是线程安全的,因为它是一个无状态操作。
    • @John:没有解释清楚但仍然有价值的部分是,委托可能会被执行多次,前提是键的值一旦没有改变委托已生成一个新值,然后将插入新值,否则 - 如果在委托运行时更改了值 - 它将重试。
    【解决方案2】:

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

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

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

    注意两点。

    • 没有努力同步委托的执行。
    • 委托可能会被多次执行,因为它们是在内部循环中调用的。

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

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

    【讨论】:

    • @Luciano:我刚刚检查过,GetOrAdd 似乎只打电话给ValueFactory 一次。所以只有AddOrUpdate 有可能多次调用代表。这可能是微软的一个错误……不确定。我在answering a similar question 时发现了很长时间。
    • @BrianGideon:当然,抱歉,我没有注意到您在谈论 AddOrUpdate 而不是 GetOrAdd。感谢您的回答。
    • 到目前为止,这种行为是否有任何改变?
    • @SebastianGodelet:我不确定。它看起来更像是一个错误,因此这种行为最终可能会得到“修复”。
    • 我理解女士的观点“不要呼吁我们对此表示支持”或“安全,待在家里”,这推动了这一实施。这是一个0%风险的观点。但它被“并发”形容词所暗示,以至于我们现在都有非工作代码!至少可以选择加入文档中的警告......我很幸运我发现了一些奇怪的东西并挖掘它......否则非工作代码将被发布给很多客户。
    猜你喜欢
    • 2019-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-07
    相关资源
    最近更新 更多