使用ReaderWriterLockSlim.EnterXXX()模式并配合try-finally语句块是否完全安全?

8

MSDN文档以及许多使用ReaderWriterLockSlim类的示例都建议使用以下模式:

cacheLock.EnterWriteLock();
try
{
    //Do something
}
finally
{
    cacheLock.ExitWriteLock();
}

但我很好奇这是否完全安全。在获取锁之后,但在try语句之前,是否可能发生某些异常,使锁定状态被卡住?最明显的候选人是ThreadAbortException。我知道这种情况的概率非常小,但后果非常严重 - 所以我认为值得思考。我不认为编译器理解此模式并防止处理器在try语句之前中断线程。

如果存在理论上的不安全可能性,那么有没有方法使其更安全?


如果文档是正确的,那么'ReaderWriterLockSlim'是线程安全的,而且这个代码示例也是尽可能线程安全的。你可能更应该担心cahceLock的处理。 - Jodrell
这种模式与Monitor.EnterThreadAbortionException的交互非常微妙。请参阅Eric Lippert博客上的C# IL代码生成的微妙之处。在C# 4中,lock模式生成不同的代码以避免这种情况。 - CodesInChaos
2个回答

8

在理论上,这段代码只有三种可能的失败情况:

  • 您已经提到了ThreadAbortException。这个问题很容易处理:确保您从不调用Thread.Abort()。您几乎肯定不需要这样做;通常有更好的方法来实现所需的结果。

    只有当你真的非常非常非常需要调用它时,并且你正在终止一个有可能保持锁打开的线程,请将整个代码块(从EnterExit)放在一个空的try...finally子句中。只有当前的finally处理程序完成时,Thread.Abort()才会抛出ThreadAbortException异常。

  • StackOverflowException是另一种可能性。它可能发生在调用ExitWriteLock期间。这也很容易处理:当堆栈溢出发生时,进程将被终止。您无法捕获或处理它。由于进程被终止,您的进程中没有其他线程会保持任何锁打开状态。

  • OutOfMemoryException在调用ExitWriteLock期间理论上可能会抛出。与StackOverflowException不同,这个问题在理论上是可处理的。如果您不捕获它,进程将再次被终止,并且您的进程中没有其他线程将保持任何锁打开状态。但是,如果您捕获它,您不能指望正确处理此异常,很可能您的其他线程也会很快抛出此异常。

总而言之,我不会担心这个问题。


把所有东西都放在 finally 块里是个好主意。我完全忘记了这种可能性。我知道调用 Abort() 是不好的实践,但如果你开发一个可重用的库,你无法控制它。 - Sasha

7

在某些高负载场景下,这可能是一个问题。 这篇文章进一步阐述了这个问题,但基本上归结为使用空try块与finally来获取和释放锁:

var lockIsHeld = false;
try {
   try {
   }
   finally {
      rwl.EnterReadLock();
      lockIsHeld = true;
   }

   // Do work here
}
finally {
   if (lockIsHeld) {
      rwl.ExitReadLock();
   }
}

这种方法确保你的锁定始终被获取并释放,因为在发生 ThreadAbortException 的情况下,finally 块保证会运行。
其他异常情况请参考 @hvd 的帖子。
个人观点是,除非你真正在现场看到这个问题,否则不必担心它...

2
使用委托参数,您可以将此模式封装在一个方法中,以便调用者看起来像 rwl.UsingReadLock(() => {...}) - CodesInChaos
有趣。我的理解是,try并没有被翻译成CPU指令 - 它只是标记了代码中一个区域的开始,该区域可以应用catch/finally,因此在第一个try和第二个try之间不可能发生异常(它们都将指向rwl.EnterReadLock()指令)。然后,lockIsHeld变量就是不必要的。 - EM0
1
好的,也许是这样的:编译器似乎可以在两个“try”语句之间生成一个无操作指令? https://blogs.msdn.microsoft.com/ericlippert/2009/03/06/locks-and-exceptions-do-not-mix/ - EM0

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