MSDN文档是这么说的

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

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


当前回答

这里有一个更简单的例子(来自这里的问题34),为什么锁(this)是不好的,并且当你的类的消费者也试图锁定对象时可能会导致死锁。 下面,三个线程中只有一个线程可以继续,其他两个线程处于死锁状态。

class SomeClass { public void SomeMethod(int id) { **lock(this)** { while(true) { Console.WriteLine("SomeClass.SomeMethod #" + id); } } } } class Program { static void Main(string[] args) { SomeClass o = new SomeClass(); lock(o) { for (int threadId = 0; threadId < 3; threadId++) { Thread t = new Thread(() => { o.SomeMethod(threadId); }); t.Start(); } Console.WriteLine(); }

为了解决这个问题,这家伙使用了Thread。TryMonitor(带超时)而不是lock:

班长。TryEnter(temp, millisecondsTimeout, ref lockWasTaken); 如果(lockWasTaken) { doAction (); } 其他的 { 抛出新的异常(" cannot get lock"); }

https://blogs.appbeat.io/post/c-how-to-lock-without-deadlocks

其他回答

如果可以公开访问实例,就会出现问题,因为可能有其他请求正在使用相同的对象实例。最好使用私有/静态变量。

因为任何可以看到类实例的代码块也可以锁定该引用。您希望隐藏(封装)锁定对象,以便只有需要引用它的代码才能引用它。关键字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.

关于它有一篇非常好的文章http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects,作者是Rico Mariani,他是Microsoft®.NET运行时的性能架构师

摘录:

这里的基本问题是您不拥有类型对象,而您 不知道还有谁能拿到。总的来说,这是一个非常糟糕的主意 依赖于锁定一个不是你创建的对象,也不知道还有谁 可能是存取。这样做会导致僵局。最安全的方法是 只锁定私有对象。

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

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

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