使用锁的最佳实践

时间:2012-08-03 02:50:58

标签: c# locking

假设我在某个类中有以下属性,其目的是用作锁。

protected object SyncRoot { get; private set; }

无论如何,无论如何以及如何设置。如果它实际上是设置的话,最好的做法是什么?

由于lock不能与null对象一起使用,我应该像这样处理吗?

lock (SyncRoot ?? new object())
    SomeMethod();

或者我应该像这样检查null吗?

if (SyncRoot != null)
    lock (SyncRoot)
        SomeMethod();
else
    SomeMethod();

如果确实如此,我想用它来锁定。否则,我不在乎。无论如何,第一种解决方案是低效还是多余的?

编辑:所有这些答案都很好。但是,我只能选一个。鉴于我与Luke讨论的情况,我的SyncRoot没有理由为null。单线程环境中锁的开销不是很大,但如果是多线程的,则是必需的。

(所有4个人的投票)感谢大家的快速回复。

6 个答案:

答案 0 :(得分:21)

我通常使用私有成员变量而不是属性,即

private static object MyLock = new object();

这种方式总是初始化。

您还可以使用非静态版本,例如

private readonly object MyLock = new object();

答案 1 :(得分:11)

同步

SyncRoot ?? new object()

没有意义,因为如果SyncRootnull,每个线程每次都会获得一个新对象。在单独的对象上进行同步没有任何效果:线程将立即继续,因为没有其他人可能在同一new对象上同步。

在第一个线程尝试获取锁之前,您应该在构造函数中初始化SyncRoot

答案 2 :(得分:3)

第一个将是一个问题,因为它不会导致任何良好的同步:

lock (SyncRoot ?? new object())
    SomeMethod();

原因在于,如果您创建一个新对象,它将被放置在堆上,但不会引用它。因此,当另一个线程出现时,它将无法找到它...它变得绝对无用,并且它不会阻止对关键部分的任何访问。

第二种方法可行,但我真的不明白为什么你想使用锁只有它。

答案 3 :(得分:2)

您最好的选择是在锁定对象的任何使用者有机会使用它之前始终初始化锁定对象。始终分配锁对象的成本很小,并且在没有线程争用时获取锁的成本很小。

因此,在代码中添加锁定/无锁检查会使代码的复杂性增加一倍,并且可能会引入细微的线程错误,但可能不会产生任何切实的性能优势。

简化您的代码:始终采取锁定。

答案 4 :(得分:2)

来自文档: https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx

  

通常,避免锁定公共类型或超出代码控制范围的实例。常见的构造锁(this),lock(typeof(MyType))和lock(“myLock”)违反了这条准则:

     
      如果可以公开访问实例,则
  • lock(this)是一个问题。
  •   如果MyType可公开访问,则
  • lock(typeof(MyType))是一个问题。
  •   
  • lock(“myLock”)是一个问题,因为进程中使用相同字符串的任何其他代码都将共享同一个锁。
  •   
     

最佳做法是定义私有对象以锁定,或私有   静态对象变量,以保护所有实例共有的数据。

样品:

 class Account
{
    decimal balance;
    private Object thisLock = new Object();

    public void Withdraw(decimal amount)
    {
        lock (thisLock)
        {
            if (amount > balance)
            {
                throw new Exception("Insufficient funds");
            }
            balance -= amount;
        }
    }
}

答案 5 :(得分:0)

如果需要对队列、集合和字典等对象进行线程安全访问,我会锁定该对象本身,不需要单独的锁定对象。最好是队列、集合或字典是私有的或只读的,这样您就可以确保它们始终是同一个对象。

示例:

class Processor
{
    readonly Queue<int> processQueue = new Queue<int>();

    public void AddToQueue(int index)
    {
        lock (processQueue)
        {
            processQueue.Enqueue(index);
        }
    }
}