【问题标题】:ConcurrentDictionary: How do I add if value does not exist or condition fails?ConcurrentDictionary:如果值不存在或条件失败,我该如何添加?
【发布时间】:2017-07-15 15:05:04
【问题描述】:

我正在尝试为ConcurrentDictionary 实现扩展方法,如果键存在,则该方法在功能上从字典中获取值。如果键不存在或条件(predicate)失败,它会尝试通过调用valueFactory 来获取值。下面是我写的方法。

public static TValue ConditionalGetOrAdd<TKey, TValue>(
    this ConcurrentDictionary<TKey, TValue> dictionary,
    TKey key,
    Func<TValue, bool> predicate,
    Func<TKey, TValue> valueFactory)
{
    TValue value = dictionary.GetOrAdd(key, valueFactory);

    if (!predicate(value))
    {
        value = valueFactory(key);
        dictionary[key] = value;
    }

    return value;
}

这里有两个问题。如果predicate 返回false,我可能会调用valueFactory 两次。这将是多余的操作。此外,整个方法不是原子的。如果一个线程在if 块内,则另一个线程不会等待它完成,然后再调用GetOrAdd 或predicate。

是否有可能在不锁定整个字典的情况下完成这项工作?就我而言,valueFactory 成本高(在内部发出 HTTP 请求),而 predicate 相当便宜。如果您有解决方案,请告诉我。如果可以达到目的,我可以修改整个方法。

【问题讨论】:

  • 如果谓词检查显然不依赖于它是否在字典中,为什么不能将它移到顶部?
  • predicate 将采用我们从字典中获得的value。除非我先打电话给TryGet 或GetOrAdd,否则我将无法知道该值。如果我不理解评论,请告诉我。
  • 啊,我现在明白了。似乎您应该只做一个普通的TryGet() 而不是GetOrAdd(),因为您不想添加它?这样,当谓词失败时,您最多只会调用一次valueFactory。
  • 谢谢。那我该如何防止比赛条件。我不想在线程调用valueFactory 时,其他线程也进入相同的状态。 @john,我会看看 AddOrUpdate 是否适合我。给我一些时间。

标签: c# concurrency


【解决方案1】:

感谢@john 的建议。 AddOrUpdate 应该为我工作。在下面添加新的方法体:

return dictionary.AddOrUpdate(
     key,
     valueFactory,
     (k, v) => predicate(v) ? v : valueFactory(k));

我会将其保留为扩展方法,而不是直接在调用者中调用 AddOrUpdate,因为它允许我在上面的两个地方重用 valueFactory 委托。

【讨论】:

  • 请注意,AddOrUpdate 是 not atomic。对于给定的键,值工厂可能会被多次调用。
  • 哇!这很令人惊讶。感谢您的提醒。我检查了这个答案:stackoverflow.com/a/10487983/470119。似乎AddOrUpdate 方法将交替调用addValueFactory/updateValueFactory 直到TryAdd/TryUpdate 分别在工厂返回的值上成功。我会确保多次调用valueFactory 不会产生副作用。
  • 这并不奇怪。 ConcurrentDictionary 类的所有 xORy 样式函数都有很好的记录,即在类本身持有的任何锁之外调用任何委托。
猜你喜欢
  • 1970-01-01
  • 2023-02-02
  • 2011-01-10
  • 1970-01-01
  • 2021-09-08
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多