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

Sil*_*ent 20 c# synchronization exception-handling

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

try  
{  
   lock(syncblk)  
   {  
        // do some processing  
    }  

}  
catch(Exception e)  
{  
    // do something with exception  
}  
Run Code Online (Sandbox Code Playgroud)

Eri*_*ert 41

我需要处理锁定块中的代码可能抛出异常的事实

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

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

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

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

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

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

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

这就是为什么你不应该在锁中抛出异常的原因; 它彻底击败了锁定的目的!锁的整个要点是确保除了负责清理混乱的线程之外,所有线程始终观察到状态是一致的.

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

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

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

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