锁语句 - 它是否总是会释放锁?

6
我最近阅读了Eric Lippert关于C#中锁实现的这篇文章,但依然有一些问题待解决。
在4.0实现中,如果在finally块中执行Monitor.Exit(temp)之前发生线程中止或任何跨线程异常,那么对象上的锁是否会保持?
在这个级别上是否有可能发生异常,导致对象仍处于锁定状态?

3
你可以尝试自己做这个。使用Monitor.Enter在另一个线程上锁定一个对象,调用Thread.Abort并查看是否可以让另一个线程进入该对象的锁定状态。 - vcsjones
1
Thread.Abort方法会将异常沿着堆栈向上传播,并在CLR终止线程之前执行finally块。 - Gusdor
1
Thread.Abort是跨线程异常唯一需要担心的情况吗?还有OutOfMemory或任何未管理的异常会使CLR进程继续运行,但只会在这个特定点杀死这个线程? - Rod
在这两种情况下,执行过程在改变某些状态时停止了(如果不是这样,那你为什么还要持有锁呢?)。在这里,唯一合理的选择是中止,否则你只会造成更多的伤害。 - Voo
2个回答

14

在4.0实现中,如果在线程的finally块中执行Monitor.Exit(temp)之前发生线程中止或任何跨线程异常 - 是否会保持对象的锁定状态?

让我们看看这段代码,以便其他读者可以清楚地了解:

bool lockWasTaken = false;
var temp = obj;
try 
{ 
  Monitor.Enter(temp, ref lockWasTaken); 
  { 
    body 
  } 
}
finally 
{ 
  if (lockWasTaken) 
  {
    // What if a thread abort happens right here?
    Monitor.Exit(temp); 
  }
}

由于您的问题基于错误的假设,即线程中止可能发生在 finally 块的中间,因此您的问题无法获得答案。

线程中止不可能发生在 finally 块的中间。这只是许多原因中的一个,说明为什么您永远不应该尝试中止线程。整个线程可能正在运行 finally 块,因此不能被中止。

在此级别是否有可能发生异常,使对象仍处于锁定状态?

不会。线程中止将延迟到控制离开 finally。解锁有效锁并不会分配内存或引发其他异常。


9

了解关于 ThreadAbortException 的相关信息:

当此异常被触发时,运行时会在结束线程之前执行所有的finally块。

(这包括在调用Thread.Abort时正在当前执行的任何finally块)

是的,锁仍然会被释放。不过,这是否值得提倡是一个非常不同的问题 - 您并不知道线程即将释放锁 - 它可以在任何地方,并且可能在变异锁保护的状态的中间阶段 - 因此,一如既往的建议是避免使用Thread.Abort


你能看一下我上面的评论吗?我知道这是一个非常模糊的问题,但我想了解所有可能的情况。 - Rod
@Rod - OutOfMemory在推理上有点令人头疼,因为如果你的finally块中的任何代码想要分配内存,则显然会失败。但我认为lock语句的unlock代码应该属于不太可能分配内存的代码集合。我想不出任何情况下解锁会失败,而且认为赢得两周的彩票或在两台独立的计算机上生成两个相同的GUID要更有可能。 - Damien_The_Unbeliever
1
释放有效的锁定永远不会失败,也不会分配内存。 - Eric Lippert
3
您的回答传达了正确的想法,但您可能希望强调的关键推理部分是:如果 finally 块已经在运行,则线程中止异常会被延迟到它完成之后 - Eric Lippert
1
如果你遇到了OutOfMemoryException,那么你的问题更大。寻找优雅地处理它的方法是浪费精力 - 相反,应该解决它,否则你将始终看到副作用。 - Gusdor
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接