MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
MSDN文档是这么说的
public class SomeObject
{
public void SomeOperation()
{
lock(this)
{
//Access instance variables
}
}
}
是“如果实例可以公开访问,则会出现问题”。我想知道为什么?这是因为锁持有的时间比需要的时间长吗?还是有更阴险的原因?
当前回答
因为如果人们可以获得你的对象实例(即:你的this)指针,那么他们也可以尝试锁定相同的对象。现在他们可能没有意识到您在内部锁定了这个,所以这可能会导致问题(可能是死锁)
除此之外,这也是一种糟糕的做法,因为它锁定了“太多”
例如,你可能有一个成员变量List<int>,你唯一需要锁定的就是这个成员变量。如果你在你的函数中锁定了整个对象,那么其他调用这些函数的东西将被阻塞等待锁定。如果这些函数不需要访问成员列表,则会导致其他代码等待,并毫无理由地降低应用程序的速度。
其他回答
抱歉,伙计们,我不同意锁定这个可能会导致僵局的说法。你混淆了两件事:僵局和饥饿。
如果不中断其中一个线程,就无法取消死锁,因此进入死锁后就无法退出 饥饿将在其中一个线程完成其工作后自动结束
这里有一张图可以说明两者的区别。
结论 如果线程饥饿对您来说不是问题,您仍然可以安全地使用lock(this)。你仍然需要记住,当线程使用lock(this)饿死线程时,它将以锁定对象的锁结束,它最终将以永远饥饿结束;)
以下是一些更容易遵循的示例代码(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永远不会因为死锁而结束。
以下是不建议使用的原因。
短版: 考虑下面的代码片段:
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"); } }
这里有一个更简单的例子(来自这里的问题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
关于它有一篇非常好的文章http://bytes.com/topic/c-sharp/answers/249277-dont-lock-type-objects,作者是Rico Mariani,他是Microsoft®.NET运行时的性能架构师
摘录:
这里的基本问题是您不拥有类型对象,而您 不知道还有谁能拿到。总的来说,这是一个非常糟糕的主意 依赖于锁定一个不是你创建的对象,也不知道还有谁 可能是存取。这样做会导致僵局。最安全的方法是 只锁定私有对象。