为什么我们需要在 C# 中锁定和对象?
Why do we need to lock and object in C#?
这是我一直不明白的事情。创建一个得到 lock
ed 的虚拟对象几乎像是一种 hack,就像示例
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;
}
}
}
来自 https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx。
为什么语言设计者不能做到
class Account
{
decimal balance;
public void Withdraw(decimal amount)
{
lock
{
if (amount > balance)
{
throw new Exception("Insufficient funds");
}
balance -= amount;
}
}
}
是否等价?
传递给 lock
的实例用于识别临界区。
您的代码中可能有任意数量的不相关关键部分,每个关键部分将锁定不同的对象。一个无参数的 lock
语句,就像你建议的那样,将无法区分许多关键部分。
编辑
虽然这看起来很明显,但值得注意的是,需要进入给定临界区的每个部分都必须能够访问锁定的对象。所以这不是在 lock
语句之前和在相同范围内创建任意实例的问题。
我认为混淆在于 lock 关键字的作用。这并不是说只有 1 个线程可以进入那段代码,而是说两件事:
- 只有拥有thisLock的线程才能进入这段代码
- 除此线程之外的任何线程也不允许使用此锁锁定的任何其他部分,因为此线程具有此锁。
你的建议是只做第一个而不是两个。看这个例子:
class Account
{
decimal balance;
private Object thisLock = new Object();
private Object thisLock2 = new Object();
public void Withdraw(decimal amount)
{
lock (thisLock)
{
if (amount > balance)
{
throw new Exception("Insufficient funds");
}
balance -= amount;
}
// more code here but no locking necessary...
lock(thisLock)
{
// only one thread can enter here who has thisLock
}
lock (thisLock2)
{
// If T1 (thread1) is working with thisLock, T2 can come here since it has nothing to do
// with thisLock.
}
}
public void AnotherOperation()
{
lock (thisLock)
{
// code here...
}
}
public void YetAnotherOperation()
{
lock (thisLock)
{
// code here...
}
}
}
当一个线程,比如 T1,正在使用第一个锁执行取款部分时,class 的所有其他带有锁(thisLock)的部分也不允许任何其他线程进入。但是有thisLock2的部分是允许其他线程进入的。
想到lock关键字的最好方法,至少在我学习的时候它帮助了我,就是把它想成一个人质。换句话说,当代码的某些部分正在执行时,它需要在您的示例中劫持人质(thisLock)。因此,一旦 thisLock 被作为人质,在该线程释放人质之前,没有其他线程可以将其作为人质。因此,所有其他也需要相同人质的代码部分将变得不可用。
这是我一直不明白的事情。创建一个得到 lock
ed 的虚拟对象几乎像是一种 hack,就像示例
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;
}
}
}
来自 https://msdn.microsoft.com/en-us/library/c5kehkcz.aspx。
为什么语言设计者不能做到
class Account
{
decimal balance;
public void Withdraw(decimal amount)
{
lock
{
if (amount > balance)
{
throw new Exception("Insufficient funds");
}
balance -= amount;
}
}
}
是否等价?
传递给 lock
的实例用于识别临界区。
您的代码中可能有任意数量的不相关关键部分,每个关键部分将锁定不同的对象。一个无参数的 lock
语句,就像你建议的那样,将无法区分许多关键部分。
编辑
虽然这看起来很明显,但值得注意的是,需要进入给定临界区的每个部分都必须能够访问锁定的对象。所以这不是在 lock
语句之前和在相同范围内创建任意实例的问题。
我认为混淆在于 lock 关键字的作用。这并不是说只有 1 个线程可以进入那段代码,而是说两件事:
- 只有拥有thisLock的线程才能进入这段代码
- 除此线程之外的任何线程也不允许使用此锁锁定的任何其他部分,因为此线程具有此锁。
你的建议是只做第一个而不是两个。看这个例子:
class Account
{
decimal balance;
private Object thisLock = new Object();
private Object thisLock2 = new Object();
public void Withdraw(decimal amount)
{
lock (thisLock)
{
if (amount > balance)
{
throw new Exception("Insufficient funds");
}
balance -= amount;
}
// more code here but no locking necessary...
lock(thisLock)
{
// only one thread can enter here who has thisLock
}
lock (thisLock2)
{
// If T1 (thread1) is working with thisLock, T2 can come here since it has nothing to do
// with thisLock.
}
}
public void AnotherOperation()
{
lock (thisLock)
{
// code here...
}
}
public void YetAnotherOperation()
{
lock (thisLock)
{
// code here...
}
}
}
当一个线程,比如 T1,正在使用第一个锁执行取款部分时,class 的所有其他带有锁(thisLock)的部分也不允许任何其他线程进入。但是有thisLock2的部分是允许其他线程进入的。
想到lock关键字的最好方法,至少在我学习的时候它帮助了我,就是把它想成一个人质。换句话说,当代码的某些部分正在执行时,它需要在您的示例中劫持人质(thisLock)。因此,一旦 thisLock 被作为人质,在该线程释放人质之前,没有其他线程可以将其作为人质。因此,所有其他也需要相同人质的代码部分将变得不可用。