c#中的锁定方法和线程安全方法的调用?

时间:2017-09-16 21:57:02

标签: c# multithreading deadlock

我应该为线程锁定一部分在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),我是否需要锁定方法块的弹出?我想,如果我锁定方法和调用方法,它会创建一个线程死锁。在这种情况下,创建线程安全代码的更好方法是什么?

感谢您的回答!

1 个答案:

答案 0 :(得分:1)

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

在您的情况下,仅锁定_objects方法内的Eject访问权限就足够了(请参阅下面的代码)。因此不需要锁定方法调用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)进行锁定的原因,因为这样的情况不会发生(尽管它不排除其他可能的死锁情况)。