В С#, как я могу безопасно выйти из блокировки с помощью блока catch try внутри?

Вот пример исключения, происходящего внутри блокировки, с блоком try-catch.

int zero = 0;
int j = 10;

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

Как безопасно освободить эту блокировку в catch?

Ответы

Ответ 1

Не будет ли он выпущен автоматически?

Из средства блокировки MSDN

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

Поэтому вам не нужно беспокоиться.

Ответ 2

Блокировка не будет выпущена до тех пор, пока вы не выйдете из области блокировки (sharedResource.SyncRoot). lock (sharedResource.SyncRoot) {} в основном идентичен:

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

Вы можете либо сделать Enter/Exit самостоятельно, если хотите больше контроля, либо просто перемотать блокировку на то, что хотите, например:

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

Ответ 3

Доказательство.

.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$2$0000)
  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

Ответ 4

Jaredpar опубликовал ссылку в комментарии, который, как мне кажется, стоит проверить:

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

В этом блоге Эрик Липперт комментирует проблемы, связанные с блокировкой в ​​С#:

Проблема заключается в том, что если компилятор генерирует команду no-op между входом монитора и защищенный от опроса регион, то это возможно, чтобы среда выполнения исключение прерывания потока после войдите в монитор, но перед попыткой. В этот сценарий, наконец, никогда не запускается поэтому утечка замка, возможно, в конечном итоге блокировка программы. Это было бы если бы это было невозможно в неоптимизированные и оптимизированные сборки.

Ответ 5

Ваш код в порядке. lock(sth){...} преобразуется внутренне в блок try finally.

Ответ 6

Не работает ли он так, независимо от того:

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

Ответ 7

Блокировка будет освобождена при выходе из контекста этого блока, однако это произойдет. В приведенном выше примере кода блокировка будет автоматически, безопасно выпущена, так как управление завершает заключительный контекст.