【问题标题】:Guarantees on documented/implicitly documented/undocumented behavior保证记录/隐含记录/未记录的行为
【发布时间】:2014-06-08 13:49:08
【问题描述】:

查看 ConcurrentDictionary 文档,它说以下内容:

表示一个线程安全的键/值对集合,可以 多个线程同时访问。

现在阅读本文时,我觉得我可以调用 ConcurrentDictionary API 中的任何方法,并且它是线程安全的……但这意味着也包括显式实现,我有这个保证吗?

我的例子是如果我想要一个原子操作从 ConcurrentDictionary 中删除一个项目,如果它的值是某个值。

所以我可以这样做:

var concurrentDictionary = new ConcurrentDictionary<string, string>();
concurrentDictionary.TryAdd("hey", "ho");

((ICollection<KeyValuePair<string, string>>) concurrentDictionary).Remove(new KeyValuePair<string, string>("hey", "ho"));

现在我查看了源代码,该操作既是原子安全的又是线程安全的,但它不在 ConcurrentDictionary API 上的事实是否意味着我不应该使用它……或者我正在使用收集做一些我不应该用它做的事情。

我可以更进一步,编写如下扩展方法:

public static boolean TryRemove(this ICollection<KeyValuePair<TKey, TValue>> collection, TKey key, TValue value)
{
    return collection.Remove(new KeyValuePair<TKey, TValue>(key, value));
}

这将出现在 ConcurrentDictionary 的 Intellisense 中,因为它实现了 ICollection 接口,许多开发人员甚至可能不知道有什么不好的地方(如果真的有什么不好的地方?!)

编辑: 我所说的“隐式记录”的意思是 ConcurrentDictionary 实现了一组接口。它的文档说它的线程是安全的,但没有说明它是否仅适用于该页面上列出的方法,而是暗示实例上的所有操作都是安全的。

【问题讨论】:

  • @SLaks,对……唔……我的周末阅读能力不是很好。我将删除我的评论以避免进一步混淆......
  • 显式 ICollection.Remove() 方法已经调用 TryRemove(),所以你的扩展方法没有用。您只能保证 ConcurrentDictionary 是线程安全的,您所做的事情不会继承该保证。
  • @HansPassant - 仅供参考,它调用私有(内部) TryRemove 方法,该方法以原子方式执行操作:return TryRemoveInternal(keyValuePair.Key, out throwAwayValue, true, keyValuePair.Value);

标签: c# .net concurrentdictionary


【解决方案1】:

这是一个关于记录行为的问题。通常,您只能依靠记录在案的行为来实际持有。其他行为可以随时更改(在运行时、应用程序运行之间、框架补丁级别之间...)。

如果您可以在文档中找到参考证明这是安全的,那么您可以这样做。

如果不是,我通常会非常小心。另一方面,ConcurrentDictionary 是核心类型,BCL 团队对其工作采用了极端的兼容性标准。即使在没有记录的生产应用程序中,我也可以这样做。他们非常小心,即使在主要框架版本之间也不会中断调用者。

由于这些兼容性保证以及这是一种核心类型,我可以从 source code 获取知识。

【讨论】:

    【解决方案2】:

    你不会通过做你描述的事情来破坏 ConcurrentDictionary 的原子性。

    您可能会导致并发字典中所有存储桶的不必要锁定。 (通过以某种方式将其作为集合访问)

    这会杀死字典的“并发”部分,并且效率低于仅用锁包围普通字典。

    ConcurrentDictionary 令人敬畏的部分不在于它是原子的——这可以很容易地用锁来完成,而是它是并发的,这意味着多个线程可以原子地写入它而不必相互等待。

    一旦您对此有了更好的了解,您就会喜欢 GetOrAdd、TryRemove 和 TryUpdate 之类的东西,因为它们允许明确转换到一个状态 - 即使您不确定之前的状态。

    【讨论】:

    • 回复:You will cause unnecessary locks of all the buckets in concurrent dictionary. (by accessing it as a collection)...我不同意。看这里:referencesource.microsoft.com/#mscorlib/system/Collections/… 我可以看到它调用了TryRemoveInternal(),它只锁定了它从中删除的存储桶。
    • 你是对的。删除调用 tryremove。其他访问它的方式可能会导致问题。一些 ConcurrentDictionary 调用也需要获取所有锁,但这没有很好的文档记录。
    猜你喜欢
    • 2011-06-22
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 2015-11-24
    • 1970-01-01
    • 2018-12-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多