为什么锁对象必须是静态的?


112

使用私有静态只读对象进行多线程锁定非常普遍。我了解到,private通过拧紧封装减少了锁定对象的入口,因此可以访问最基本的对象。

但是为什么要静态呢?

private static readonly object Locker = new object();

最后,该字段仅在我的班级中使用,我也可以只使用它:

private readonly object Locker = new object();

任何意见?

更新:

作为示例,我粘贴了此代码(仅作为示例)。我可以在此上使用静态或非静态储物柜,并且两者都可以正常工作。考虑下面的答案,我应该像这样定义我的储物柜?(对不起,下周我要接受采访,需要了解每个细节:)

private readonly object Locker = new object();

这是代码:

    private int _priceA;
    private int _priceB;
    private EventWaitHandle[] _waithandle;
    private readonly IService _service;

//ctor
public ModuleAViewModel(IService service)
    {
        _service = service;
        _modelA = new ModelA();
        _waithandle = new ManualResetEvent[2];
        _waithandle[0] = new ManualResetEvent(false);
        _waithandle[1] = new ManualResetEvent(false);
        LoadDataByThread();
    }


 private void LoadDataByThread()
        {
            new Thread(() =>
                           {
                               new Thread(() =>
                               {
                                   lock (Locker)
                                   {
                                       _priceA = _service.GetPriceA();
                                   }
                                   _waithandle[0].Set();
                               }).Start();

                               new Thread(() =>
                               {
                                   lock (Locker)
                                   {
                                       _priceB = _service.GetPriceB();
                                   }
                                   _waithandle[1].Set();
                               }).Start();

                               WaitHandle.WaitAll(_waithandle);
                               PriceA = _priceA;
                               PriceB = _priceB;
                           }).Start();
        }

谢谢


15
据我所知,静态通常用于使其与实例无关。如果存在多个“ MyWorkerClass”实例,则一次只能运行一个给定的数据(假设它们都使用共享资源)。
布拉德·克里斯蒂

2
该编辑缺少一个重要的细节:位置_service_waithandle位置?实例?静态的?其他?例如,这可能是故意同步对远程服务器的访问...
Marc Gravell

正确,进行第二次编辑:是的,从这件事开始,您可以按实例锁定。但是,可能有使其保持静态的原因-如果原始开发人员想要(如上所述)同步访问权限,以便服务器一次仅从此AppDomain收到一个请求...我不知道是否是这种情况,或者是否只是偶然的。
马克·格雷韦尔

Answers:


177

“使用私有静态只读对象进行多线程锁定不是很常见”,而是以适当/选定的粒度使用锁定。有时候就是这样static。IMO通常不是,而是基于实例的。

您看到static锁定的主要时间是用于全局缓存,或用于延迟加载全局数据/单例。在后者中,无论如何都有更好的方法。

因此,这实际上取决于:Locker您的方案如何使用?它在保护本身就是静态的东西吗?如果是这样,则锁应该是静态的。如果保护的是基于实例的内容,则IMO锁应基于实例。


24
您能否详细介绍延迟加载全局数据的更好方法?
bizi 2013年

我总是使用static / volatile,因为如果有多个地方都基于实例,我仍然希望控制我的方法/变量以线程安全的方式被访问。许多实例可能正在访问相同的资源,我想控制它。我也希望看到这样做的更好。您的代表非常出色,我相信您的回复对我来说也同样重要。请回复?
安德鲁·辛普森

82

它不必是静态的,其实有时它应该不会是一成不变的。

该变量应与用于锁定的方法处于同一范围内。如果方法是静态的,则变量应为静态,如果方法是实例的方法,则变量应为实例变量。

当用于锁定实例方法时,静态变量仍然可以使用,但是您将被锁定过多。您将锁定所有实例中的所有方法,而不仅仅是同一实例中的方法。


28
+1为“ a-ha” ... 您将锁定所有实例中的所有方法,而不仅仅是同一实例中的方法。
Radarbob 2012年

3
@radarbob-次要细节:您不会锁定所有可能只对更多客户端感兴趣的方法,而不会锁定所有方法。方法永远不会被锁定,只是采用了互斥锁。
Erno 2014年

我怀疑此答案的措词可能会误导他人-锁定不应与方法范围做任何事情-它仅应与那些方法中访问的共享数据的范围有关。实例方法可能不会访问任何共享数据(因此不需要锁定),可能会访问静态共享数据(因此需要静态锁定,因此重构也可能是个好主意),与静态方法相同……
Alexei Levenkov

@AlexeiLevenkov:正确的是,范围实际上应该由数据是否是静态的来确定,但是方法的范围也应该由数据来确定,因此它们都可以组合在一起。实例数据通常不需要锁定,但是如果实例在线程之间共享,则需要锁定。
Guffa

28

锁的范围和寿命可以/应该取决于您要锁定的“事物”。静态锁通常用于锁定静态事物。


3
次要详细信息:锁不是静态的,用于标识锁的对象是静态的。另一个小细节:您不会锁定“事物”。
Guffa 2014年

2
是的,我认为如果我们试图锁定错误的“事物”,它们可能太大而又结实,有一天可以逃脱。
ProfK

12
@Guffa这很奇怪,您在上面的评论中正确地说:“您只是在使事情复杂化,”现在我看到1分钟才说,似乎您在使事情复杂化:)
Nicholas Petersen
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.