在try中通过锁定锁定资源。 这是错的吗?

使用带有try块的锁有什么问题吗? 我记得在某个地方读过我们应该总是尝试在try块中放入最少量的代码并在内部锁定自己使用try-finally块,你们在这里看到了什么错误。我需要处理锁内的代码这一事实块可以抛出exception

try { lock(syncblk) { // do some processing } } catch(Exception e) { // do something with exception } 

我需要处理这个锁块中的代码可能抛出exception的事实

这是你的问题。 这是一个可怕的情况。

你为什么要先锁定? 通常你锁定某些东西的原因是你想要实现以下逻辑:

  • 把门锁上
  • 做一个烂摊子
  • 清理
  • 打开门

如果你这样做,那么没有一个人能够尊重锁着的门,看到了这个烂摊子。

例如,您可能希望以线程安全的方式交换变量“left”和“right”的值,因此您:

  • 拿锁
  • 将左变量读入tempLeft
  • 将正确的变量读入tempRight
  • 写tempLeft到右边
  • 我们弄得一团糟; “正确”的原始价值已经消失
  • 把tempRight写进左边
  • 我们已经清理了一塌糊涂,再次与世界相处融洽
  • 释放锁

现在假设在发生混乱之后抛出exception。 怎么了? 我们直接跳到解锁,留下另一个线程看到的混乱

这就是为什么你不应该在锁中抛出exception的原因; 它彻底击败了锁定的目的! 锁的整个要点是确保所有线程始终遵守状态,除了负责清理混乱的线程。

如果你有一个可以从锁中抛出的exception,最好的办法就是摆脱那种可怕的局面。 如果你不能这样做,那么确保你可以(1)一旦exception逃脱锁定就完全破坏进程,这样你所造成的混乱不会导致数据丢失或其他伤害 – 做一个FailFast和从轨道上对这个过程进行核对,这是确定的唯一方法 – 或者(2)编写回滚代码,撤销退出锁之前你正在尝试的任何操作; 也就是说,将混乱清理回原始状态。

如果后者是你的策略,那么不要把try块放在 ; 它在那里没用,因为即时控件通过exception离开锁定,另一个线程可能会崩溃和死亡,因为你暴露在它的混乱 。 将处理exception的try 放在锁内:

 lock(whatever) { try { MakeAMess(); } finally { CleanItUp(); // Either by completing the operation or rolling it back // to the pre-mess state } } 

如果您有很强的可靠性要求,那么处理可能引发exception的锁定关键部分是一项极其困难的编程任务,最好留给专家; 如果您发现自己处于这种情况下,可以考虑使用受约束的执行区域

我认为您可以按照自己的方式进行操作,但这里有关于锁定的MSDN描述以供您参考。 有关详细信息,请参阅http://msdn.microsoft.com/en-us/library/ms173179.aspx 。

使用锁(C#)或SyncLock(Visual Basic)关键字通常比直接使用Monitor类更受欢迎,因为锁或SyncLock更简洁,并且因为锁或SyncLock确保释放底层监视器,即使受保护的代码也是如此抛出一个例外。 这是通过finally关键字完成的,该关键字执行其关联的代码块,无论是否抛出exception。

所以我不确定你指的是什么样的例外,但如果你担心的是你可能因为例外而无法释放锁,你不必担心它。

您总是可以使用更长的语法,如下所示:

 System.Threading.Monitor.Enter(x); try { ... } catch(Exception e) { } finally { System.Threading.Monitor.Exit(x); }