MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
当前回答
...同样的论点也适用于这个结构:
lock(typeof(SomeObject))
其他回答
...同样的论点也适用于这个结构:
lock(typeof(SomeObject))
关于它有一篇非常好的文章http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects,作者是Rico Mariani,他是Microsoft®.NET运行时的性能架构师
摘录:
这里的基本问题是您不拥有类型对象,而您 不知道还有谁能拿到。总的来说,这是一个非常糟糕的主意 依赖于锁定一个不是你创建的对象,也不知道还有谁 可能是存取。这样做会导致僵局。最安全的方法是 只锁定私有对象。
以下是一些更容易遵循的示例代码(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永远不会因为死锁而结束。
因为任何可以看到类实例的代码块也可以锁定该引用。您希望隐藏(封装)锁定对象,以便只有需要引用它的代码才能引用它。关键字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.