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"); } }

其他回答

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

lock(typeof(SomeObject))

你可以建立一个规则,规定一个类可以有锁定'this'或类中代码实例化的任何对象的代码。所以只有不遵循这个模式才会有问题。

如果您想保护自己不受不遵循此模式的代码的影响,那么接受的答案是正确的。但如果遵循这个模式,那就不是问题。

锁的优点是效率高。如果你有一个简单的“值对象”,它只包含一个值。它只是一个包装器,它被实例化了数百万次。通过要求仅为锁定而创建一个私有同步对象,基本上使对象的大小增加了一倍,分配的数量也增加了一倍。当性能很重要时,这是一个优势。

当您不关心分配数量或内存占用时,出于其他答案中指出的原因,避免锁定(这)是可取的。

看看MSDN主题线程同步(c#编程指南)

Generally, it is best to avoid locking on a public type, or on object instances beyond the control of your application. For example, lock(this) can be problematic if the instance can be accessed publicly, because code beyond your control may lock on the object as well. This could create deadlock situations where two or more threads wait for the release of the same object. Locking on a public data type, as opposed to an object, can cause problems for the same reason. Locking on literal strings is especially risky because literal strings are interned by the common language runtime (CLR). This means that there is one instance of any given string literal for the entire program, the exact same object represents the literal in all running application domains, on all threads. As a result, a lock placed on a string with the same contents anywhere in the application process locks all instances of that string in the application. As a result, it is best to lock a private or protected member that is not interned. Some classes provide members specifically for locking. The Array type, for example, provides SyncRoot. Many collection types provide a SyncRoot member as well.

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

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

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

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

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

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"); } }