【问题标题】:Locking method and calls of method for thread-safety in c#?c#中线程安全的锁定方法和方法调用?
【发布时间】:2017-09-18 01:37:14
【问题描述】:

我应该为线程锁定在c#中调用线程安全方法的部分代码吗?

例如:

T Eject (bool? state)
    {
        lock (this)
        {
            //somecode
            return _objects[i];
        }
    }

    public T TakeNew()
    {
        return Eject(null);
    }

    public T Reserve()
    {
        return Eject(true);
    }

或者我应该像这样锁定返回:

lock(this)
    {       
        return Eject(true);
    }

如果我锁定所有调用 Eject(true),我是否需要锁定方法 Eject 块?我认为,如果我锁定方法和方法调用,它会造成线程死锁。在这种情况下,创建线程安全代码的更好方法是什么?

感谢您的回答!

【问题讨论】:

    标签: c# multithreading deadlock


    【解决方案1】:

    最好的做法是只锁定尽可能少的代码,因为这样可以最大限度地减少锁争用的可能性(从而提高性能和可伸缩性),并且还可以提高代码的可读性(更容易看到哪些对象受锁保护)。

    在您的情况下,仅在 Eject 方法中锁定对 _objects 的访问就足够了(请参见下面的代码)。锁定方法调用return Eject(true) 是不必要的(但如果你这样做,它不会导致死锁)。

    使用this 进行锁定也被认为是不好的做法,因为这会在某些情况下导致死锁。因此专用对象通常存储在封闭类的私有或受保护字段中并用于锁定(再次参见下面的代码)。

    所以这应该没问题:

    class Foo<T>
    {
        // Dedicated object instance used for locking
        private Object m_MyLock = new Object();
    
        T Eject(bool? state)
        {
            lock (m_MyLock)//You typically use the same locking object everywhere within the class
            {
                //somecode
                return _objects[i];
            }
        }
    
        string SomeOtherMethod()//Some other method that needs to be threadsafe too
        {
            lock (m_MyLock)//You typically use the same locking object everywhere within the class
            {
                return _objects[i].ToString();
            }
        }
    
        public T TakeNew()
        {
            return Eject(null);
        }
    
        public T Reserve()
        {
            return Eject(true);
        }
    }
    

    编辑:

    澄清 OP 的评论:您不应该为每个锁创建新的锁定对象。您通常在类中使用相同的锁定对象。当你在你的类中使用lock(this) 并且代码的另一部分不知道它(例如第三方库代码)使用你的类的实例作为锁定时,我正在谈论的死锁情况可能会发生以这种不幸的方式对象,导致死锁。在Why is lock(this) {…} bad? 中更好地解释了这种情况(包括这种情况的示例)。这就是为什么你应该使用你的私有对象(m_MyLock)进行锁定,因为这样就不会发生这种情况(尽管它不排除其他可能的死锁情况)。

    【讨论】:

    • 感谢您的回答!我真的明白吗:对于每个lock,我应该创建new locker,以避免在少数线程方法使用单个储物柜时出现死锁?
    • @ИванПогосов:不,您不应该为每个lock 创建新的锁定对象。我将编辑我的答案以更好地解释它。
    猜你喜欢
    • 1970-01-01
    • 2012-03-20
    • 1970-01-01
    • 2016-08-30
    • 1970-01-01
    • 1970-01-01
    • 2016-09-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多