Ответ 1
Не будет ли он выпущен автоматически?
Из средства блокировки MSDN
System.Threading.Monitor.Enter(x);
try {
...
}
finally {
System.Threading.Monitor.Exit(x);
}
Поэтому вам не нужно беспокоиться.
Вот пример исключения, происходящего внутри блокировки, с блоком 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?
Не будет ли он выпущен автоматически?
Из средства блокировки MSDN
System.Threading.Monitor.Enter(x);
try {
...
}
finally {
System.Threading.Monitor.Exit(x);
}
Поэтому вам не нужно беспокоиться.
Блокировка не будет выпущена до тех пор, пока вы не выйдете из области блокировки (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.
}
Доказательство.
.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
Jaredpar опубликовал ссылку в комментарии, который, как мне кажется, стоит проверить:
http://blogs.msdn.com/ericlippert/archive/2009/03/06/locks-and-exceptions-do-not-mix.aspx
В этом блоге Эрик Липперт комментирует проблемы, связанные с блокировкой в С#:
Проблема заключается в том, что если компилятор генерирует команду no-op между входом монитора и защищенный от опроса регион, то это возможно, чтобы среда выполнения исключение прерывания потока после войдите в монитор, но перед попыткой. В этот сценарий, наконец, никогда не запускается поэтому утечка замка, возможно, в конечном итоге блокировка программы. Это было бы если бы это было невозможно в неоптимизированные и оптимизированные сборки.
Ваш код в порядке. lock(sth){...}
преобразуется внутренне в блок try finally
.
Не работает ли он так, независимо от того:
try
{
lock (sharedResource.SyncRoot)
{
int bad = 2 / 0;
}
}
catch (DivideByZeroException e)
{
// exception caught but lock not released
}
finally
{
//release lock
}
Блокировка будет освобождена при выходе из контекста этого блока, однако это произойдет. В приведенном выше примере кода блокировка будет автоматически, безопасно выпущена, так как управление завершает заключительный контекст.