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

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

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

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

Ответ 1

Первый; вы считали 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, что может быть проблемой.

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

Ответ 2

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

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

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

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

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

Ответ 3

да, это будет нормально выпущено; lock действует как try/finally, а в конце - Monitor.Exit(myLock), поэтому независимо от того, как вы его выходите, будет выпущено. В качестве примечания лучше избегать catch(... e) {throw e;}, так как это наносит ущерб трассировке стека на e; лучше не ловить его вообще или использовать: throw; вместо throw e;, который выполняет повторный бросок.

Если вы действительно хотите знать, блокировка в С# 4/.NET 4:

{
    bool haveLock = false;
    try {
       Monitor.Enter(myLock, ref haveLock);
    } finally {
       if(haveLock) Monitor.Exit(myLock);
    }
} 

Ответ 4

"Оператор блокировки скомпилирован для вызова Monitor.Enter, а затем пытается... наконец-то блокировать. В блоке finally вызывается Monitor.Exit.

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

Взято из: Этот сайт

Ответ 5

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

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

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

Ответ 6

Просто добавь немного к замечательному ответу Марка.

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

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