MSDN文档是这么说的

public class SomeObject
{
  public void SomeOperation()
  {
    lock(this)
    {
      //Access instance variables
    }
  }
}

是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?


当前回答

以下是不建议使用的原因。

短版: 考虑下面的代码片段:

object foo = new Object(); 
object bar = foo; 

lock(foo)
{
  lock(bar){}
}  

这里,foo和bar引用的是导致死锁的同一个对象实例。这是现实中可能发生的事情的简化版本。

长版: 为了根据下面的代码片段更详细地解释它,假设您编写了一个类(在本例中为SomeClass),并且类的使用者(名为“John”的编码器)希望获得类实例(在本例中为someObject)上的锁。他遇到死锁是因为他在实例someObject上获得了一个锁,在这个锁中他调用了该实例的一个方法(SomeMethod()),该方法在内部获得了同一个实例上的锁。

I could have written the following example with or without Task/Thread and the gist of deadlock still remains the same. To prevent bizarre situation where the main Thread finishes while its children are still running, I used .Wait(). However, in long-running-tasks or situation where a code-snippet executes more frequently, you would definitely see the same behavior. Although John applied a bad practice of using an instance of a class as a lock-object, but we (as the developer of a classlibrary SomeClass) should deter such situation simple by not using this as a lock-object in our class. Instead, we should declare a simple private field and use that as our lock-object. using System; using System.Threading; using System.Threading.Tasks; class SomeClass { public void SomeMethod() { //NOTE: Locks over an object that is already locked by the caller. // Hence, the following code-block never executes. lock (this) { Console.WriteLine("Hi"); } } } public class Program { public static void Main() { SomeClass o = new SomeClass(); lock (o) { Task.Run(() => o.SomeMethod()).Wait(); } Console.WriteLine("Finish"); } }

其他回答

因为如果人们可以获得你的对象实例(即:你的this)指针,那么他们也可以尝试锁定相同的对象。现在他们可能没有意识到您在内部锁定了这个,所以这可能会导致问题(可能是死锁)

除此之外,这也是一种糟糕的做法,因为它锁定了“太多”

例如,你可能有一个成员变量List<int>,你唯一需要锁定的就是这个成员变量。如果你在你的函数中锁定了整个对象,那么其他调用这些函数的东西将被阻塞等待锁定。如果这些函数不需要访问成员列表,则会导致其他代码等待,并毫无理由地降低应用程序的速度。

...同样的论点也适用于这个结构:

lock(typeof(SomeObject))

想象一下,你的办公室有一位技术娴熟的秘书,这是部门的共享资源。偶尔,你会因为有任务而冲向他们,只希望你的另一个同事还没有认领他们。通常你只需要等很短的一段时间。

因为关心就是分享,你的经理决定顾客也可以直接使用秘书。但这有一个副作用:当您为该客户工作时,客户甚至可能会认领它们,并且您还需要他们执行部分任务。死锁发生,因为声明不再是层次结构。如果从一开始就不允许客户索赔,这完全可以避免。

lock(this) is bad as we've seen. An outside object might lock on the object and since you don't control who's using the class, anyone can lock on it... Which is the exact example as described above. Again, the solution is to limit exposure of the object. However, if you have a private, protected or internal class you could already control who is locking on your object, because you're sure that you've written your code yourself. So the message here is: don't expose it as public. Also, ensuring that a lock is used in similar scenario's avoids deadlocks.

与此完全相反的是锁定整个应用领域共享的资源——这是最坏的情况。这就像把你的秘书放在外面,让外面的人去认领一样。结果是彻底的混乱——或者就源代码而言:这是一个坏主意;把它扔掉,重新开始。我们怎么做呢?

正如这里大多数人指出的那样,类型在应用领域是共享的。但是我们可以使用更好的东西:字符串。原因是字符串被池化了。换句话说,如果你有两个字符串在一个应用域中有相同的内容,它们有可能有完全相同的指针。由于指针被用作锁键,基本上你得到的是“为未定义的行为做好准备”的同义词。

类似地,你不应该锁定WCF对象HttpContext。目前,线程。当前,单例(一般)等。避免这一切的最简单方法是什么?private [static] object myLock = new object();

我知道这是一个旧线程,但因为人们仍然可以查找并依赖它,因此有必要指出lock(typeof(SomeObject))明显比lock(this)差。话虽如此;对Alan指出lock(typeof(SomeObject))是不好的做法表示由衷的赞扬。

System的一个实例。Type是最通用的粗粒度对象之一。至少是System的一个实例。Type对于AppDomain是全局的,. net可以在一个AppDomain中运行多个程序。这意味着,如果两个完全不同的应用程序都试图在System.Type的同一个全局实例上获得同步锁,那么它们可能会对彼此造成潜在的干扰,甚至产生死锁。

因此,lock(这)并不是特别健壮的形式,可能会导致问题,并且由于所引用的所有原因应该总是令人惊讶。然而,像log4net这样广泛使用、相对受人尊敬且明显稳定的代码广泛使用锁(此)模式,尽管我个人更希望看到这种模式发生变化。

但是lock(typeof(SomeObject))打开了一个全新的增强的蠕虫容器。

不管怎样。

以下是不建议使用的原因。

短版: 考虑下面的代码片段:

object foo = new Object(); 
object bar = foo; 

lock(foo)
{
  lock(bar){}
}  

这里,foo和bar引用的是导致死锁的同一个对象实例。这是现实中可能发生的事情的简化版本。

长版: 为了根据下面的代码片段更详细地解释它,假设您编写了一个类(在本例中为SomeClass),并且类的使用者(名为“John”的编码器)希望获得类实例(在本例中为someObject)上的锁。他遇到死锁是因为他在实例someObject上获得了一个锁,在这个锁中他调用了该实例的一个方法(SomeMethod()),该方法在内部获得了同一个实例上的锁。

I could have written the following example with or without Task/Thread and the gist of deadlock still remains the same. To prevent bizarre situation where the main Thread finishes while its children are still running, I used .Wait(). However, in long-running-tasks or situation where a code-snippet executes more frequently, you would definitely see the same behavior. Although John applied a bad practice of using an instance of a class as a lock-object, but we (as the developer of a classlibrary SomeClass) should deter such situation simple by not using this as a lock-object in our class. Instead, we should declare a simple private field and use that as our lock-object. using System; using System.Threading; using System.Threading.Tasks; class SomeClass { public void SomeMethod() { //NOTE: Locks over an object that is already locked by the caller. // Hence, the following code-block never executes. lock (this) { Console.WriteLine("Hi"); } } } public class Program { public static void Main() { SomeClass o = new SomeClass(); lock (o) { Task.Run(() => o.SomeMethod()).Wait(); } Console.WriteLine("Finish"); } }