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


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

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

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

6 69

6 ответов:

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

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

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

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

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

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

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

да, что выпустит правильно;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);
    }
} 

"оператор блокировки компилируется в вызов для мониторинга.Введите, а затем попробуйте ... наконец блок. В последнем блоке, монитор.Выход называется.

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

взяты из: этот сайт

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

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

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

просто чтобы добавить немного к превосходному ответу Марка.

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

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