12

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

protected object SyncRoot { get; private set; }

无论如何,无论如何设置以及是否设置。如果它实际上是设置的,那么使用它的最佳实践是什么?

由于锁不适用于空对象,我应该这样处理吗?

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

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

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

如果它实际上是设置的,我想用它来锁定。否则,我不在乎。无论如何,第一个解决方案是否效率低下或多余?

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

(为你们 4 人投票)谢谢大家的快速回复。

4

6 回答 6

22

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

private static object MyLock = new object();

这样它总是被初始化。

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

private readonly object MyLock = new object();
于 2012-08-03T02:53:11.047 回答
13

同步开启

SyncRoot ?? new object()

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

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

于 2012-08-03T03:01:09.797 回答
4

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

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

原因是如果您创建一个新对象而不将其分配给它,SyncRoot它将被放置在堆上,但不会引用它。所以当另一个线程来的时候,它不会找到它......它变得绝对没用,它不会阻止对关键部分的任何访问。

第二种方法会起作用,尽管我真的不明白为什么你想使用只有可用的锁。

于 2012-08-03T03:00:01.610 回答
3

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

通常,避免锁定公共类型或超出代码控制范围的实例。常见的构造 lock (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;
        }
    }
}
于 2016-03-01T14:15:50.027 回答
2

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

因此,在您的代码中添加锁定/无锁定检查将使代码的复杂性加倍,并可能引入微妙的线程错误,但可能不会产生任何明显的性能优势。

简化你的代码:总是拿锁。

于 2012-08-03T03:02:48.103 回答
0

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

样本:

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

    public void AddToQueue(int index)
    {
        lock (processQueue)
        {
            processQueue.Enqueue(index);
        }
    }
}
于 2021-03-06T11:04:20.667 回答