MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
当前回答
看看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.
其他回答
这里有一个更简单的例子(来自这里的问题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指向当前类实例,因此任何数量的东西都可以引用它,并可以使用它来进行线程同步。
需要明确的是,这很糟糕,因为其他一些代码块可能会使用类实例来锁定,并且可能会阻止您的代码获得及时的锁定,或者可能会产生其他线程同步问题。最好的情况是:没有其他方法使用对您的类的引用来锁定。中间情况:某些东西使用对你的类的引用来锁,这导致了性能问题。最坏的情况:某些东西使用你的类的引用来进行锁,这会导致非常糟糕、非常微妙、非常难以调试的问题。
...同样的论点也适用于这个结构:
lock(typeof(SomeObject))
以下是一些更容易遵循的示例代码(IMO):(将在LinqPad中工作,参考以下名称空间:System。Net和System.Threading.Tasks)
需要记住的一点是,lock(x)基本上是语法糖,它所做的就是使用Monitor。输入,然后使用try、catch和finally块调用Monitor.Exit。参见:https://learn.microsoft.com/en-us/dotnet/api/system.threading.monitor.enter(备注部分)
或者使用c#锁语句(Visual Basic中的SyncLock语句), 它将Enter和Exit方法包装在一个try…finally块中。
void Main()
{
//demonstrates why locking on THIS is BADD! (you should never lock on something that is publicly accessible)
ClassTest test = new ClassTest();
lock(test) //locking on the instance of ClassTest
{
Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Parallel.Invoke(new Action[]
{
() => {
//this is there to just use up the current main thread.
Console.WriteLine($"CurrentThread {Thread.CurrentThread.ManagedThreadId}");
},
//none of these will enter the lock section.
() => test.DoWorkUsingThisLock(1),//this will dead lock as lock(x) uses Monitor.Enter
() => test.DoWorkUsingMonitor(2), //this will not dead lock as it uses Montory.TryEnter
});
}
}
public class ClassTest
{
public void DoWorkUsingThisLock(int i)
{
Console.WriteLine($"Start ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
lock(this) //this can be bad if someone has locked on this already, as it will cause it to be deadlocked!
{
Console.WriteLine($"Running: ClassTest.DoWorkUsingThisLock {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
Console.WriteLine($"End ClassTest.DoWorkUsingThisLock Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
}
public void DoWorkUsingMonitor(int i)
{
Console.WriteLine($"Start ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
if (Monitor.TryEnter(this))
{
Console.WriteLine($"Running: ClassTest.DoWorkUsingMonitor {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
Monitor.Exit(this);
}
else
{
Console.WriteLine($"Skipped lock section! {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
}
Console.WriteLine($"End ClassTest.DoWorkUsingMonitor Done {i} CurrentThread {Thread.CurrentThread.ManagedThreadId}");
Console.WriteLine();
}
}
输出
CurrentThread 15
CurrentThread 15
Start ClassTest.DoWorkUsingMonitor 2 CurrentThread 13
Start ClassTest.DoWorkUsingThisLock 1 CurrentThread 12
Skipped lock section! 2 CurrentThread 13
End ClassTest.DoWorkUsingMonitor Done 2 CurrentThread 13
注意线程#12永远不会因为死锁而结束。
看看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.