В приложении для потоковой передачи С #, если бы я заблокировал объект, скажем, очередь, и если произойдет исключение, останется ли объект заблокированным? Вот псевдокод:
int ii;
lock(MyQueue)
{
MyClass LclClass = (MyClass)MyQueue.Dequeue();
try
{
ii = int.parse(LclClass.SomeString);
}
catch
{
MessageBox.Show("Error parsing string");
}
}
Насколько я понимаю, код после перехвата не выполняется - но мне было интересно, будет ли освобождена блокировка.
Ответы:
Первый; вы рассматривали TryParse?
in li; if(int.TryParse(LclClass.SomeString, out li)) { // li is now assigned } else { // input string is dodgy }
Блокировка будет снята по 2 причинам; во-первых,
lock
по сути:Monitor.Enter(lockObj); try { // ... } finally { Monitor.Exit(lockObj); }
Второй; вы перехватываете и не
lock
генерируете повторно внутреннее исключение, поэтому на самом деле оно никогда не видит исключения. Конечно, вы удерживаете блокировку на время MessageBox, что может быть проблемой.Таким образом, он будет выпущен во всех, кроме самых фатальных, катастрофических и невосстановимых исключений.
источник
TheadAbortException
междуMonitor.Enter
иtry
: blogs.msdn.com/ericlippert/archive/2009/03/06/…Замечу, что никто не упомянул в своих ответах на этот старый вопрос, что снятие блокировки исключения - невероятно опасная вещь. Да, операторы блокировки в C # имеют семантику «finally»; когда элемент управления выходит из замка нормально или ненормально, замок снимается. Вы все говорите об этом так, будто это хорошо, но это плохо! Правильный поступок, если у вас есть заблокированная область, которая вызывает необработанное исключение, - это немедленно завершить зараженный процесс до того, как он уничтожит больше пользовательских данных , а не освобождать блокировку и продолжать работу. .
Посмотрите на это так: представьте, что у вас есть ванная с замком на двери и очередь людей, ожидающих снаружи. Бомба в ванной взорвалась, убив человека там. Ваш вопрос: «Будет ли в этой ситуации замок автоматически разблокирован, чтобы следующий человек мог попасть в ванную комнату?» Да, это будет. Это нехорошо. Там просто взорвалась бомба и кого-то убили! Водопровод, вероятно, разрушен, дом больше не является прочным, и там может быть еще одна бомба . Правильнее всего вывести всех как можно быстрее и снести весь дом.
Я имею в виду, подумайте над этим: если вы заблокировали область кода, чтобы читать из структуры данных без ее изменения в другом потоке, и что-то в этой структуре данных вызывало исключение, велики шансы, что это потому, что структура данных коррумпирован . Пользовательские данные теперь испорчены; вы не хотите пытаться сохранить данные пользователя на этом этапе, потому что затем вы сохраняете поврежденные данные. Просто прекратите процесс.
Если вы заблокировали область кода, чтобы выполнить мутацию без одновременного чтения состояния другим потоком, и мутация выдает ошибку, то, если данные не были повреждены раньше, это точно теперь . Это именно тот сценарий, от которого должна защищать блокировка . Теперь коду, который ожидает чтения этого состояния, немедленно будет предоставлен доступ к поврежденному состоянию и, возможно, произойдет сбой. Опять же, правильное решение - прекратить процесс.
Как ни крути, исключение внутри блокировки - плохая новость . Правильный вопрос - не "будет ли моя блокировка очищена в случае исключения?" Правильный вопрос, который следует задать: «Как мне гарантировать, что внутри блокировки никогда не будет исключения? И если есть, то как мне структурировать мою программу так, чтобы мутации возвращались к предыдущему хорошему состоянию?»
источник
да, это выпустит правильно;
lock
действует какtry
/finally
с theMonitor.Exit(myLock)
в finally, поэтому независимо от того, как вы выйдете, он будет выпущен. В качестве примечанияcatch(... e) {throw e;}
лучше избегать, так как это повреждает трассировку стекаe
; лучше не поймать его на всех , или в качестве альтернативы: использование ,throw;
а неthrow e;
который делает повторный бросок.Если вы действительно хотите знать, блокировка в C # 4 / .NET 4:
{ bool haveLock = false; try { Monitor.Enter(myLock, ref haveLock); } finally { if(haveLock) Monitor.Exit(myLock); } }
источник
"Оператор блокировки компилируется для вызова Monitor.Enter, а затем для блока try… finally. В блоке finally вызывается Monitor.Exit.
Генерация JIT-кода как для x86, так и для x64 гарантирует, что прерывание потока не может произойти между вызовом Monitor.Enter и блоком try, который непосредственно следует за ним ".
Взято с: Этот сайт
источник
Monitor.Enter
иtry
, так что условие «немедленно следует» для JIT нарушен.Ваш замок будет разблокирован правильно. А
lock
действует так:try { Monitor.Enter(myLock); // ... } finally { Monitor.Exit(myLock); }
И
finally
блоки гарантированно будут выполнены, независимо от того, как вы выходите изtry
блока.источник
Просто чтобы добавить немного к отличному ответу Марка.
Подобные ситуации и есть причина существования
lock
ключевого слова. Это помогает разработчикам убедиться, что блокировка снята вfinally
блоке.Если вы вынуждены использовать
Monitor.Enter
/,Exit
например, для поддержки тайм-аута, вы должны обязательно разместить вызовMonitor.Exit
вfinally
блоке, чтобы обеспечить надлежащее снятие блокировки в случае исключения.источник