Блокируется ли заблокированный объект, если в нем возникает исключение?

В приложении ac # threading, если бы я должен был заблокировать объект, скажем, очередь, и если произойдет исключение, останется ли объект заблокированным? Вот псевдокод:

int ii; lock(MyQueue) { MyClass LclClass = (MyClass)MyQueue.Dequeue(); try { ii = int.parse(LclClass.SomeString); } catch { MessageBox.Show("Error parsing string"); } } 

Как я понимаю, код после того, как catch не выполняется, – но мне было интересно, освободится ли блокировка.

Первый; вы считали TryParse?

 in li; if(int.TryParse(LclClass.SomeString, out li)) { // li is now assigned } else { // input string is dodgy } 

Блокировка будет выпущена по двум причинам; во-первых, lock по существу:

 Monitor.Enter(lockObj); try { // ... } finally { Monitor.Exit(lockObj); } 

Во-вторых, вы ловите и не перебрасываете внутреннее исключение, поэтому lock никогда не видит исключения. Конечно, вы держите блокировку на протяжении всего MessageBox, что может быть проблемой.

Таким образом, он будет выпущен во всех, кроме самых фатальных катастрофических неисправимых исключениях.

Я отмечаю, что никто не упомянул в ответах на этот старый вопрос, что освобождение блокировки от исключения является невероятно опасной задачей. Да, операторы блокировки в C # имеют «окончательную» семантику; когда управление выходит из замка нормально или ненормально, замок блокируется. Вы все говорите об этом, как будто это хорошо, но это плохо! Правильная вещь, если у вас есть заблокированная область, которая бросает необработанное исключение, – это прекратить больной процесс непосредственно перед тем, как он уничтожит больше пользовательских данных , а не освободит блокировку и продолжит работу .

Посмотрите на это так: предположим, у вас есть ванная комната с замком на двери и линия людей, ожидающих снаружи. Бомба в ванной гаснет, убивая человека там. Ваш вопрос: «В этой ситуации замок будет автоматически разблокирован, чтобы следующий человек мог попасть в ванную?» Да, это будет. Это нехорошо. Бомба только что ушла и убила кого-то! Водопровод, вероятно, разрушен, дом уже не является структурно прочным, и там может быть еще одна бомба . Правильное дело – вытащить всех как можно быстрее и уничтожить весь дом.

Я имею в виду, подумайте: если вы заблокировали область кода, чтобы читать из структуры данных, не будучи мутированной в другом streamе, и что-то в этой структуре данных породило исключение, вероятность хорошая, потому что структура данных является коррумпированным . Пользовательские данные теперь перепутаны; вы не хотите пытаться сохранить данные пользователя на этом этапе, потому что вы затем сохраняете поврежденные данные. Просто прекратите процесс.

Если вы заблокировали область кода, чтобы выполнить мутацию без другого streamа, считывающего состояние в одно и то же время, и мутация бросает, то, если раньше данные не были повреждены, это точно . Это именно тот сценарий, который блокировка должна защищать . Теперь код, который ждет, чтобы прочитать это состояние, немедленно получит доступ к поврежденному состоянию и, вероятно, сам потерпит крах. Опять же, правильная вещь – прекратить процесс.

Независимо от того, как вы его срезаете, исключение внутри блокировки – плохая новость . Правильный вопрос, который нужно задать, – это «не будет ли мой замок очищен в случае исключения?» Правильный вопрос: «Как я могу гарантировать, что внутри блокировки никогда не будет исключения? И если есть, то как мне создать свою программу, чтобы мутации откатывались назад к предыдущим хорошим состояниям?»

да, это будет правильно выпущено; lock действует как try / finally , с помощью Monitor.Exit(myLock) в конце, так что независимо от того, как вы его выходите, будет выпущен. В качестве побочной заметки лучше всего избегать 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, а затем пытается … наконец-то блокировать. В блоке finally вызывается Monitor.Exit.

Генерация кода JIT для x86 и x64 гарантирует, что прерывание streamа не может произойти между вызовом Monitor.Enter и блоком try, который сразу же следует за ним ».

Снято с: Этот сайт

Ваш замок будет выпущен правильно. lock действует следующим образом:

 try { Monitor.Enter(myLock); // ... } finally { Monitor.Exit(myLock); } 

И, finally блоки гарантированно выполняются независимо от того, как вы покинете блок try .

Просто добавим немного превосходного ответа Марка.

Ситуации, подобные этому, являются самой причиной существования ключевого слова lock . Это помогает разработчикам убедиться, что блокировка выпущена в блоке finally .

Если вы вынуждены использовать Monitor.Enter / Exit например, для поддержки тайм-аута, вы должны обязательно поместить вызов Monitor.Exit в блок finally чтобы обеспечить надлежащий выпуск блокировки в случае исключения.

  • Является ли инициализация статической членской переменной C ++ streamобезопасной?
  • JTextFields поверх активного чертежа на JPanel, проблемы с streamами
  • Вызывающий stream не может получить доступ к этому объекту, потому что ему принадлежит другой stream. WPF
  • Как распространять исключения между streamами?
  • Каков правильный способ обработки событий на C ++?
  • Что такое семафор?
  • Анимированная анимация Swing
  • Почему большинство пользовательских интерфейсов однопоточных?
  • есть ли в java функция «block until condition станет true»?
  • В ArrayBlockingQueue зачем копировать конечное поле участника в локальную конечную переменную?
  • Программа зависает, если stream создается в статическом блоке инициализатора
  • Давайте будем гением компьютера.