C# 为什么这段代码不会以死锁结束

C# 为什么这段代码不会以死锁结束,c#,.net,c#-4.0,locking,C#,.net,C# 4.0,Locking,我有一个C#代码: 因此,如果我执行GetInt2,基本上我希望出现死锁,但代码只是执行而已。任何好的解释 锁阻塞正在执行的线程,除非该线程已经持有对象的锁 在这种情况下,只有一个线程在执行;它在GetInt2中获取lockValue上的锁,然后进入GetInt1,在那里它再次在lockValue上遇到一个lock语句,它已经持有该语句,因此可以继续 C#中的lock语句是语法糖,编译器将其解释为调用。(在“监视器”部分)是 相当于 System.Object obj = (System.Obj

我有一个C#代码:


因此,如果我执行
GetInt2
,基本上我希望出现死锁,但代码只是执行而已。任何好的解释

锁阻塞正在执行的线程,除非该线程已经持有对象的锁


在这种情况下,只有一个线程在执行;它在
GetInt2
中获取
lockValue
上的锁,然后进入
GetInt1
,在那里它再次在
lockValue
上遇到一个lock语句,它已经持有该语句,因此可以继续

C#中的
lock
语句是语法糖,编译器将其解释为调用。(在“监视器”部分)是

相当于

System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}
Monitor.Enter的文档说明

同一线程多次调用
Enter
是合法的 无阻塞;但是,必须执行相同数量的
Exit
调用 在等待对象的其他线程解除阻止之前调用


从上面可以明显看出,只要只涉及一个线程,给定的代码就不会产生死锁。

这里的一般情况是同步对象是否可重入。换句话说,如果同一线程已经拥有锁,则可以再次获取。另一种说法是对象是否具有“线程关联”

在.NET中,监视器类(实现lock语句)、互斥和ReaderWriterLock是可重入的。Semaphore和SemaphoreSlim类不是,您可以使用二进制信号量使代码死锁。实现锁定最便宜的方法是使用Interlocked.CompareExchange(),它也不会重新进入

使同步对象重新进入需要额外的成本,它需要跟踪哪个线程拥有它,以及在拥有它的线程上获得锁的频率。它需要存储Thread.ManagedId和一个计数器,两个int。这影响了C++中的选择,例如C++ 11语言规范,最后将线程添加到标准库。std::mutex类在该语言中不是可重入的,添加递归版本的建议被拒绝。他们认为让it重新进入的开销太高了。也许有点笨手笨脚,相对于调试意外死锁所花费的时间来说,这是一个相当小的成本:)但在这种语言中,获取线程ID可以像在.NET中一样便宜,这是一个不难做到的事情


这在ReaderWriterLockSlim类中公开,您可以选择。请注意RecursionPolicy属性,它允许您在NoRecursion和SupportsRecursion之间进行选择。NoRecursion模式更便宜,使它真正纤细。

是-重要的一点是,允许同一线程进入它已经持有的任何锁,任何次数。信号量按照我的预期工作,这是我的代码,它只是
private static ss=new SemaphoreSlim(1,1)和在代码中我包装了
ss.Wait()
ss.Release()
进入try/finally块。答案很好+1解释所有锁的工作原理,而不仅仅是被问到的锁
lock (x)
{
    DoSomething();
}
System.Object obj = (System.Object)x;
System.Threading.Monitor.Enter(obj);
try
{
    DoSomething();
}
finally
{
    System.Threading.Monitor.Exit(obj);
}