使用ReaderWriterLockSlim.EnterXXX()的模式与随后的try-finally子句是否完全安全

Sas*_*sha 7 .net c# multithreading readerwriterlockslim

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

cacheLock.EnterWriteLock();
try
{
    //Do something
}
finally
{
    cacheLock.ExitWriteLock();
}
Run Code Online (Sandbox Code Playgroud)

但我很好奇它是否完全安全.获取锁之后是否可能发生某些异常,但是在try语句之前锁定被锁定状态?最明显的候选人是ThreadAbortException.我知道这种情况的可能性非常小,但后果极其糟糕 - 所以我认为值得考虑一下.我不相信编译器理解这种模式并阻止处理器在try语句之前中断线程.

如果理论上有可能这个代码不安全,有没有办法让它更安全?

小智 8

理论上只有三种方法可以让我想到:

  • ThreadAbortException你已经提到了.这很容易正确处理:只是确保你永远不会打电话Thread.Abort().你几乎肯定不需要; 几乎总有更好的方法来达到预期的效果.

    只有当你真的,真的,真的需要调用它,并且你正在中止线程是一个与保持锁打开的风险,放置的代码的整个块(从EnterExit)在try... finallywhere子句中的try-block是空的.只会在当前处理程序完成时Thread.Abort()抛出.ThreadAbortExceptionfinally

  • StackOverflowException是另一种可能性.它可能发生在通话期间ExitWriteLock.这也很容易:当发生堆栈溢出时,进程终止.你不能抓住或处理这个.由于进程终止,进程中的其他任何线程都不会保持任何锁定.

  • OutOfMemoryException理论上可能会在电话会议期间被抛出ExitWriteLock.不同的是StackOverflowException,这个理论上是可捕捉的.如果你没有捕获它,那么进程将再次终止,并且进程中的其他任何线程都不会保持任何锁定.但是,如果你确实抓住了它,你就不能指望正确处理它,并且很可能,你的其他线程很快就会开始抛出这个异常.

简而言之,我不担心.


Dea*_*ard 7

它可能是一个问题,显然是在一些高负荷情况下.文章进入它进一步,但它基本上可以归结为使用再加上最后一个空try块来获取和释放锁:

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

   // Do work here
}
finally {
   if (lockIsHeld) {
      rwl.ExitReadLock();
   }
}
Run Code Online (Sandbox Code Playgroud)

这种方法可以保证您的锁始终被获取和释放,因为即使在a的情况下,也可以保证运行ThreadAbortException.

其他例外情况在@ hvd的帖子中有详细说明.

我个人不会担心,除非你真的在野外看到这个问题...

  • 使用委托参数,您可以将此模式封装在方法中,以便调用者看起来像`rwl.UsingReadLock(()=> {...})` (2认同)