【问题标题】:.NET/C# dictionary locking protocols.NET/C# 字典锁定协议
【发布时间】:2018-09-27 19:02:18
【问题描述】:

我需要准确理解 .NET 的 Dictionary 集合的锁定和并发问题——而不是 ConcurrentDictionary。假设我有一个字典定义为 key=string, value=SomeOtherRandomClass。

  1. 在调用 TryGetValue("abc") 期间是否必须对整个字典进行写锁定?

  2. 如果可以保证在调用 TryGetValue 期间不会添加/删除任何键 - 是否仍需要对整个字典进行写锁定?

  3. 如果成功调用 TryGetValue,是否必须在修改相应值 (SomeOtherClass) 时锁定整个字典?或者只是那个特定的键,这样两个人就不能同时更新同一个(SomeOtherClass)。

我认为最合乎逻辑的答案是对#3 是、否,然后是否和是。但我想绝对确定。但我可以想象#1 的答案是否定的实现。

【问题讨论】:

  • 最简单的解决方案是使用ConcurrentDictionary。使用它而不是试图重新发明轮子,你有什么具体问题? 1) 是的。 2) 不,但您保证这种情况的方式是锁定整个字典(参见 1)。 3) 这有点取决于您要做什么,实际上是 ConcurrencyDictionary 无法帮助您的事情。
  • 我认为是的,不,不,但是。在并发环境中,几乎所有查找都必须对整个字典进行写锁定(但可能会发生多个并发查找),并且对任何写操作都必须进行读/写锁定。一旦您从字典中取出对象,为了对该对象进行更改,您只需要锁定对该特定对象的访问,尽管另一个线程可以替换该键并将其从字典中完全删除。但是,API 无法保证这些。使用 ConcurrentDictionary 是确保您安全的唯一方法。
  • 您是否阅读了该类型的文档中关于如何从多个线程访问和不能访问它的内容?它专门回答了所有这些问题。
  • 我同意@MattBurland。如果您要问这些问题,您应该使用ConcurrentDictionary

标签: c# dictionary


【解决方案1】:

在调用 TryGetValue("abc") 期间是否必须对整个字典进行写锁定?

是的,因为字典的实现不是线程安全的。

如果可以保证在调用 TryGetValue 期间不会添加/删除任何键 - 是否仍需要对整个字典进行写锁定?

不,这是不可能的,是的,它仍然需要写锁定。我不知道“整个字典”是什么意思……你需要一个排他锁来防止两个线程在同一个字典中执行代码。这是因为字典的实现不是线程安全的。

如果成功调用 TryGetValue,是否必须在修改相应值 (SomeOtherClass) 时锁定整个字典?或者只是那个特定的键,这样两个人就不能同时更新同一个(SomeOtherClass)。

如果您正在修改值本身(例如,您调用 dictionary[key] = value),那么是的,它必须被锁定。如果值是引用类型,并且您没有修改引用而只是修改被引用的对象,则不需要锁定字典,因为您没有执行任何字典的代码,这不是线程安全的。

【讨论】:

  • 但是方法保证是安全的。
【解决方案2】:

Dictionary 在设计时并未考虑多线程。在Dictionary 实例上的每个 操作(读取和写入操作)必须被锁定(最好是在DictionarySyncRoot 属性上),如果它将被多个线程访问,或者您有损坏Dictionary 的风险。无法仅锁定一把特定的钥匙。

但是,Dictionary 并不关心其 的内部结构,所以一旦你获得了一个值,你就不需要在阅读时保持Dictionary 锁定或修改值本身 - 除非多个线程也将访问每个值(但您可能会仅锁定该值而逃脱)。这一切都取决于您的应用程序的要求,但您需要对ConcurrentDictionary 执行相同的工作。

还要注意ConcurrentDictionary 仅保护个人 操作。如果两个线程尝试获取某个键的值,并将该键的值更新为比以前多一个,那么您仍然会遇到竞争条件,而无需手动锁定读写操作。

【讨论】:

  • 好的,我确实看到了,但是如果你想要的记录的物理位置是通过对键应用散列函数来确定的 - 如果你的目标值的地址不能改变,我不会看不到它是如何做到的 - 然后我看不到插入/删除其他键如何移动目标记录的地址。
  • @lhs 字典的其他突变可能导致需要调整后备数组的大小,从而导致所有数据移动到新位置。
  • 好的,谢谢Servy,我明白了。但这意味着 - 如果支持数组可以随时调整大小 - 如果我有以下逻辑:
  • 哎呀,对不起。假设 (1) TryGetValue 成功,并且我有我想要的键和值。现在假设为了论证 (2) 我必须对值 (SomeOtherClass) 进行十分钟的计算。如果所有数据都可以移动,那么这意味着我必须将整个字典锁定整整十分钟,不是吗?
  • @lhs:在调整大小期间将在Dictionary 内移动的数据只是对您的值的引用 - 值本身不受影响。因此,您在处理值时固有地不必持有锁(在您从Dictionary 获得它之后,在锁下)。
猜你喜欢
  • 2010-12-29
  • 1970-01-01
  • 1970-01-01
  • 2012-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多