锁变量应该声明为volatile吗?

24

我有以下的锁语句:

private readonly object ownerLock_ = new object();

lock (ownerLock_)
{
}

我应该为我的锁变量使用volatile关键字吗?

private readonly volatile object ownerLock_ = new object();

我在MSDN上看到,volatile通常用于无需使用锁定访问的字段,因此如果我使用锁定,就不需要使用volatile吗?

来自MSDN

通常情况下,volatile修饰符用于多个线程访问一个字段而无需使用锁定语句来序列化访问。


我们需要比这更多的上下文才能回答这个问题。这个锁存在于使用锁的对象的哪个位置? - Kristian Fenn
3
当前状态下,它绝对是线程安全的。但当你在lock{}块中添加代码时,无法保证其线程安全性。 - Tetsujin no Oni
дҪ дёҚеә”иҜҘйңҖиҰҒй”Ғе®ҡдёҖдёӘreadonlyеҜ№иұЎ...еӣ дёәе®ғжҳҜreadonlyзҡ„... - NominSim
值得一读的内容,以解释锁(Lock)与易失性(Volatile)之间不同的行为和功能。 - Eoin Campbell
1
我认为他打算将它用作那个关键部分的锁杆。 - Tetsujin no Oni
2个回答

22
如果您只在拥有锁的情况下访问锁“保护”的数据,那么使这些字段具有volatile特性是多余的。您也不需要将ownerLock_变量设为volatile。(您当前没有在lock语句中显示任何实际代码,这使得很难用具体的术语进行讨论 - 但我假设您实际上会在lock语句中读取/修改一些数据。)
在应用程序代码中,volatile应该极少使用。如果您想要无锁访问单个变量,Interlocked几乎总是更容易理解。如果你需要超越这个范围的无锁访问,我几乎总是开始加锁。(或者尝试从一开始使用不可变的数据结构。)
我只期望在试图为线程构建更高级别抽象的代码中看到volatile - 例如在TPL代码库内部。它真的是专家工具,他们真正深入了解.NET内存模型...我认为这样的人非常少。

感谢您的快速回复。假设我只使用Lock,虽然这很少见,但两个线程都会在同一时间检查ownerLock_状态并尝试锁定它或彼此吗?我问这个问题是因为我在我的代码中遇到了锁定问题,我认为它与这个问题有关。 - Dor Cohen
@DorCohen:同一时间只有一个线程可以获得锁。然后它将执行“lock”语句的主体,当它释放锁时,另一个线程将能够获得锁并执行代码。 - Jon Skeet
@DorCohen,看起来你的问题不在于你锁定了什么,而是在于你在锁定体内尝试做什么。 - Tetsujin no Oni
@DorCohen - 请注意锁定的一件事是你要锁定对象的实例,而不仅仅是一个字段。因此,您需要确保每个将尝试获取锁的线程都会获得相同的“guard”对象实例。此MSDN文章提供更多信息:http://msdn.microsoft.com/zh-cn/library/ms173179(v=VS.80).aspx - Kristian Fenn
如果没有“readonly”(但锁本身实际上从未被编写),答案是否仍然适用?如果锁是可写的并且针对某些模糊的情况进行更改呢? - crokusek
@crokusek:如果锁是可写的,那么在各种方面都会有问题。如果它不是只读的,我认为还是可以的。 - Jon Skeet

3
如果某个东西是“readonly”,那么它就是线程安全的,没有别的。 (嗯,几乎是这样。 一个专家可能能够想出如何在您的“lock”语句上获得NullReferenceException,但这并不容易。)使用“readonly”关键字,您不需要使用“volatile”、“Interlocked”或锁定。 它是多线程的理想关键字,你应该尽可能地使用它。 它非常适用于锁对象,其中它的缺点(无法更改值)不重要。
此外,虽然引用是不可变的,但所引用的对象可能并非如此。 “new object()”在这里,但如果它是可变的(例如List),而且不是线程安全的,您会希望锁定该引用(以及所有其他引用,如果有)以防止两个线程同时更改对象。

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