当锁定一个变量时,是否需要在所有地方都对其进行锁定?

4
我想知道当多个线程同时访问一个变量时,每次访问都必须加锁吗?例如,我有一个列表变量,并希望在删除其中一项时不能访问该列表。即使是只访问数据的线程,也必须加锁吗?或者只需要在“删除项目”线程中加锁就足够了。我的担忧是,如果一个线程在处理列表时停在中间,转到另一个从列表中删除并锁定变量的线程,会出现错误。
4个回答

13

你的浴室门上没有锁,而是有一个双面标志,一面写着“占用”,另一面写着“未占用”。 当有人接近浴室时,如果标志被标记为“占用”,他们就等待直到标志标记为“未占用”。 如果标志被标记为“未占用”,他们将标志设置为“占用”并进入。 离开时,他们将标志设置回“未占用”。

(还必须存在一些协商机制来处理有两个或更多人等待的情况 - 谁先进去?而且,当两个人同时接近一个未占用的浴室时会发生什么 - 谁赢了?但我们将忽略这些细节;它们与类比不相关。)

这种方案完全可行。 你的问题是“如果有人忽略标志,或者忘记改变它,我会遇到两个人同时在浴室的情况吗?”

显然可以。如果你不相信我,那么我鼓励你尝试一下,看看当有人忽略协议时会发生什么。只有每个人都遵循协议,资源访问协议才能保护对该资源的访问!


10
如果你希望保持集合的一致状态(或任何其他使用锁保护的对象),你必须在读和写操作时进行加锁。
有一些“例外情况”,例如使用ReaderWriterSlimLock,你仍然需要获取写锁,但可以有效地执行多线程读取:

使用ReaderWriterLockSlim来保护一个被多个线程读取并被单个线程写入的资源。ReaderWriterLockSlim允许多个线程处于读模式,允许一个线程处于写模式并独占锁,允许一个具有读访问权限的线程处于可升级读模式,从而该线程可以升级到写模式而无需放弃其对资源的读访问权限。


即使在获取共享变量的访问权限时(一种锁定方式),这种情况下可能与其他读者共享,但归根结底,无论何时涉及到影响共享资源的所有操作,都需要请求访问权限,否则可能会遇到多线程问题。 - David Rodríguez - dribeas
问题在于我的解决方案分散在很多文件中...我想我只能将此锁传递给它们所有的文件。 - Alex
你的回答完全没有问题,但我想向OP指出,在这种情况下,ReaderWriterLockSlim 可能比普通的 lock 更慢。RWLS 的开销约为 lock 的 5 倍,因此读者的数量必须显着超过写入者,并且保持足够长的时间才能超过额外开销的盈亏平衡点。这是一个好建议,值得尝试,但如果最终不起作用,请不要感到惊讶。 - Brian Gideon
1
在这种情况下,考虑将锁和列表封装在一个组合类中,并进行适当的操作。 - Sjoerd

1

我已经问过自己这个问题几次,并意识到一旦出现这个问题,同步协议很可能被破坏。在良好的同步纪律中,甚至不会出现进行不受保护访问的欲望。

我所能想到的唯一例外是,您拥有由锁保护的某种计数器,并且您只想为统计/信息目的检查它。如果您只对此感兴趣,并且您知道对该计数器变量的读/写在您特定的平台和内存模型上是原子的(并且将来此特定软件即使在任何未来平台上运行时也是如此),则可以随意访问计数器而不使用锁定。第二个前提条件本来就很难满足,所以您不应该这样做。


这个其他问题中有更多的信息:https://dev59.com/BEjSa4cB1Zd3GeqPCAgq - Ringding

0

在对象上使用lock并不会真正锁定该对象。实际上,锁对象只是一种令牌,因此您可以锁定任何对象,只要所有线程都同意对共享状态的读写使用相同的锁定对象。约定是使用一个私有的只读Object实例,并在其上进行锁定。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接