线程安全对象 - 静态的还是非静态的?

11

最近我参加了一次面试,技术面试官问我如何使一个应用程序具有线程安全性。

在正确解释了lock()之后,他说将对象设置为静态的不是一个好主意。

private static readonly object _syncLock = new object();
他声称的原因是静态对象比非静态对象更慢,因为它对于线程锁定来说更加困难。这是真的吗?
编辑: 尽管如此,我仍然不确定。这三种方法有什么区别?
private static readonly object _syncLock = new object();
public static readonly object _syncLock = new object();
private readonly object _syncLock = new object();
6个回答

12

锁对象是否应该是静态的取决于您要锁定的对象。如果您想锁定类的一个实例,则无法使用静态锁对象。如果您想锁定静态数据,则无法使用实例锁对象。因此,似乎没有选择余地。

您可以考虑使用静态或实例锁对象来锁定对实例数据的访问,但这会导致不同的行为。使用实例锁对象只能锁定一个实例,而静态锁对象将锁定所有实例。因此,在性能调优方面也没有选择余地。


你的解释非常有道理,但不要忘记它是私有静态的。无论如何,外部都无法访问它。 - Houman
这也是我所想的。将锁对象设置为非静态会改变锁的作用域。 - Brendan Kowitz

7
他声称的原因是静态变量是在运行时而非编译时运行,这会使得该对象对线程锁定的速度比非静态变量慢。但这个解释并不太合理,我认为面试官可能不清楚自己在说什么,或者你可能误解了他的意思。

我也在想着完全相同的事情(并且打字)。 - Darren Kopp
我已经做了笔记。他说在这种情况下静态会更慢。我不是在为他辩护,我只是想找到真相。 :) - Houman
好的,各位,问题已经澄清了,你们能再读一遍吗? - Houman
@Matt,现在不考虑速度问题,下面两个代码有什么区别呢?private static readonly object _syncLock = new object(); private readonly object _syncLock = new object();别忘了,它们都是私有的!这会有什么影响吗? - Houman
第一个是静态的,第二个不是... 如果你不确定静态和非静态之间的区别,那么你可能应该多了解一下。 - matt b
显示剩余4条评论

0

如果您只有一个类的实例在多个线程之间共享,可以使用普通对象。但是,如果您有多个类的实例在多个线程之间共享,则必须使用静态对象。

另一方面,对于普通对象,您可以管理一个类的一个实例的并发性,而对于静态对象,您可以管理一个类的所有实例的并发性。


0
有时在面试中,我会说一些我知道是不正确的话或完全无意义的话,以测试候选人是否能有效地辩论自己的观点,或者只是放弃并同意我的观点。
哦,这里还有一篇Jeffrey Richter关于锁的正确使用的优秀文章。 :)

1
你不担心这会产生反作用吗?如果我遇到一个面试官持有一些错误的观点,我可能会认为该公司在技术方面相当糟糕,从而对面试留下不好的印象。 - matt b
1
不,我必须知道一个人是否认为自己是正确的,他就能够有效地论述自己的观点。否则,在我的组织中,其他开发人员会把他吃掉。 - JP Alioto
3
我认为更好的是能看到你潜在的老板能够被说服。没有比一个不能承认错误的经理更糟糕的了。 - JP Alioto
说实话,如果一家公司在面试时强迫你接受错误的范例,我会对在这样的公司工作有很大保留。我只能想象一旦你开始为他们工作,他们会让你做什么。我会合理地解释我的观点,如果面试官开始在没有理性原因的情况下向你施加压力,让你接受他的观点 - 逃离这家公司。 - galets
3
我认为JP是在说他想看看候选人是否试图纠正面试官,候选人的说服力等等,或者候选人是否听之任之甚至没有注意到这些胡言乱语。我没有看到关于欺凌的任何内容。 - matt b

0

每当您需要确保不同线程同时操作同一实例时,请使用非静态对象作为锁。

假设您有一些List类,其中包含一个特殊的Reorder方法,该方法接受一些奇怪的参数。考虑在某些并行进程期间需要重新排序100个不同的列表。您只关心不同的线程不会同时操作同一列表,因为这可能会影响您的重新排序逻辑。您不需要静态锁,因为您不关心在同时操作不同列表时发生了什么。

静态锁的一个简单示例是初始化某些静态数据,其中您希望仅运行一次加载逻辑。例如缓存或单例。


你说的都是正确的。但是请解释一下,如果对象已经是私有的,那么这样做有什么区别呢?如果它是私有的,任何线程都无法从外部访问它。那么在这种情况下拥有一个私有静态的意义是什么呢?私有会破坏静态的含义。 - Houman
并不是这样,私有性与静态性没有任何关系。事实上,由于它只是用于锁定,所以应该是私有的。您希望锁定发生在类的实现内部,因此将其暴露给外部世界是没有意义的。在加载示例中,当您调用像:MyClass.Current 这样的静态属性时,您希望锁定在内部处理。正是 Current 的实现使用锁来确保它不会两次加载实例。 - eglasius

-1
其他人正确地指出,使用静态或实例字段的选择取决于您需要锁定的状态(类级别或实例级别),而锁定本身的速度没有相关差异。但是,如果您确实只需要使用实例数据,则使用“lock(this)”可能比锁定所有线程访问任何实例的数据更快。这可能是面试官的意思——在多个线程仅使用实例数据的应用程序中,如果只锁定实例,则它确实应该更快,因为它不会阻塞其他线程使用其他实例。
相反,如果线程正在访问类级(静态)状态,则需要使用单个对象锁定它们所有。当我需要这样做时,我使用的模式是像这样锁定类类型:
[编辑-在下面的评论中看到并不是一个好主意]
lock(typeof(MyClass))
{
  // use class-level data
}

这避免了创建静态对象字段的必要性。

锁(typeof(MyClass))是个好主意。+1 但你不应该总是使用它,就像你不应该总是使用lock(this)一样。你会锁定整个实例或所有静态数据,而你可能只需要锁定其中的一小部分。创建专用的锁对象允许您在每个实例或类型中拥有多个锁,并执行更细粒度的锁定。 - Daniel Brückner
1
抱歉,我必须撤销我的投票。刚刚了解到这段代码有多糟糕。请参阅http://bytes.com/groups/net-c/249277-dont-lock-type-objects了解详情。 - Daniel Brückner
1
情况变得更糟了。lock(this)同样是一个糟糕的选择 - 详情请参见http://msdn.microsoft.com/de-de/magazine/cc188793(en-us).aspx。教训是: 只锁定引用类型的私有实例或静态对象! - Daniel Brückner
我正要说,lock(this)是另一个面试问题,说明为什么这种方法不好。它很容易导致死锁。尽管如此,我仍然不确定,对象的私有实例锁与对象的公共静态锁相比,私有静态锁有何区别。 - Houman
Daniel,在阅读了那篇文章后,我不得不同意锁定类型对象是个不好的主意。而且猜猜我是从哪里学到这个技巧的?通过使用反编译工具Reflector查看微软自己的代码!但可惜的是,我已经记不清具体在哪里看到了,因为那是很久之前的事情了。只能希望最新的框架已经对此进行了改进。 - JayMcClellan

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