在 C# 中,如何安全地退出带有 try catch 块的锁?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/639493/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-04 11:22:14  来源:igfitidea点击:

In C# how can I safely exit a lock with a try catch block inside?

c#

提问by Justin Tanner

Here is an example of an exception happening inside a lock, with a try-catch block.

这是一个在锁内发生异常的示例,带有 try-catch 块。

int zero = 0;
int j = 10;

lock (sharedResource.SyncRoot)
{
    try
    {
        j = j / zero;
    }
    catch (DivideByZeroException e)
    {
        // exception caught but lock not released
    }
}

How do I safely release this lock in the catch?

我如何安全地释放这个锁?

采纳答案by Mykola Golubyev

Won't it be released automatically?

不会自动释放吗?

From the MSDN lock means

来自MSDN的锁指

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

So you don't have to bother.

所以你不必费心。

回答by cgreeno

Wouldnt it just run like so regardless:

不管它是否会像这样运行:

try
{
  lock (sharedResource.SyncRoot)
  {
      int bad = 2 / 0;
  }
}
catch (DivideByZeroException e)
{
    // exception caught but lock not released
}
finally
{
      //release lock
}

回答by Jeff Kotula

The lock will be released when the context of it's block is exited, however that happens. In the code example given above the lock will automatically, safely be released as control exits the final } context.

当它的块的上下文退出时,锁将被释放,但是会发生这种情况。在上面给出的代码示例中,当控制退出最终的 } 上下文时,锁将自动、安全地释放。

回答by Michael

The lock won't be released until you pass out of the scope of the lock(sharedResource.SyncRoot) block. lock (sharedResource.SyncRoot) {}is basically identical to:

在您超出 lock(sharedResource.SyncRoot) 块的范围之前,不会释放锁。 lock (sharedResource.SyncRoot) {}基本上等同于:

Monitor.Enter(sharedResource.SyncRoot);
try
{
}
finally
{
    Monitor.Exit(sharedResource.SyncRoot);
}

You can either do the Enter/Exit yourself if you want more control, or just rescope the lock to what you want, like:

如果您想要更多控制,您可以自己执行 Enter/Exit,或者只是将锁定范围重新设置为您想要的,例如:

try
{
    lock(sharedResource.SyncRoot)
    {
        int bad = 2 / 0;
    }
}
catch (DivideByZeroException e)
{
   // Lock released by this point.
}

回答by Grzenio

Your code is perfectly fine. The lock(sth){...}is translated internally to a try finallyblock.

你的代码非常好。在lock(sth){...}内部转换为try finally块。

回答by annakata

Proof.

证明。

.method public hidebysig instance void  test(int32 i) cil managed
{
  // Code size       43 (0x2b)
  .maxstack  2
  .locals init ([0] int32 bad,
           [1] class [mscorlib]System.DivideByZeroException e,
           [2] object CS##代码##00)
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldfld      object WebApplication1.myclass::mutex
  IL_0007:  dup
  IL_0008:  stloc.2
  IL_0009:  call       void [mscorlib]System.Threading.Monitor::Enter(object)
  IL_000e:  nop
  .try
  {
    IL_000f:  nop
    .try
    {
      IL_0010:  nop
      IL_0011:  ldc.i4.2
      IL_0012:  ldarg.1
      IL_0013:  div
      IL_0014:  stloc.0
      IL_0015:  nop
      IL_0016:  leave.s    IL_001d
    }  // end .try
    catch [mscorlib]System.DivideByZeroException 
    {
      IL_0018:  stloc.1
      IL_0019:  nop
      IL_001a:  nop
      IL_001b:  leave.s    IL_001d
    }  // end handler
    IL_001d:  nop
    IL_001e:  nop
    IL_001f:  leave.s    IL_0029
  }  // end .try
  finally
  {
    IL_0021:  ldloc.2
    IL_0022:  call       void [mscorlib]System.Threading.Monitor::Exit(object)
    IL_0027:  nop
    IL_0028:  endfinally
  }  // end handler
  IL_0029:  nop
  IL_002a:  ret
} // end of method myclass::test

回答by Justin Tanner

Jaredpar posted a link in a comment which I think is worth checking out:

Jaredpar 在评论中发布了一个链接,我认为值得一看:

http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx

http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx

In this blog post Eric Lippert comments on the problems related to locking in C#:

在这篇博文中,Eric Lippert 评论了与 C# 中的锁定相关的问题:

The problem here is that if the compiler generates a no-op instruction between the monitor enter and the try-protected region then it is possible for the runtime to throw a thread abort exception after the monitor enter but before the try. In that scenario, the finally never runs so the lock leaks, probably eventually deadlocking the program. It would be nice if this were impossible in unoptimized and optimized builds.

这里的问题是,如果编译器在监视器进入和受保护区域之间生成无操作指令,那么运行时可能会在监视器进入之后但在尝试之前抛出线程中止异常。在这种情况下, finally 永远不会运行,因此锁泄漏,可能最终导致程序死锁。如果这在未优化和优化的构建中是不可能的,那就太好了。