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

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

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

Насколько я понимаю, код после перехвата не выполняется, но мне было интересно, будет ли освобождена блокировка.


person Khadaji    schedule 26.02.2009    source источник
comment
В качестве последней мысли (см. Обновления) - вам, вероятно, следует удерживать блокировку только на время удаления из очереди ... выполнять обработку вне блокировки.   -  person Marc Gravell    schedule 26.02.2009
comment
Код после перехвата выполняется, потому что исключение обрабатывается   -  person cjk    schedule 26.02.2009
comment
Спасибо, я, должно быть, пропустил этот вопрос, мне удалить этот вопрос?   -  person Vort3x    schedule 27.02.2012
comment
Кажется, что пример кода не подходит для этого вопроса, но вопрос вполне правильный.   -  person SalvadorGomez    schedule 11.04.2012
comment
Автор C # Designer - Блокировка & Исключение   -  person Jivan    schedule 02.03.2017


Ответы (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, что может быть проблемой.

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

person Marc Gravell    schedule 26.02.2009
comment
Я знаю о трипарсе, но это не имеет отношения к моему вопросу. Это был простой код, объясняющий вопрос, а не настоящая проблема, связанная с синтаксическим анализом. Пожалуйста, замените синтаксический анализ на любой код, который вызовет уловку и сделает вас удобнее. - person Khadaji; 26.02.2009
comment
Как насчет того, чтобы выбросить новое исключение (в иллюстративных целях); ;-п - person Marc Gravell; 26.02.2009
comment
За исключением случаев, когда между Monitor.Enter и try встречается TheadAbortException: blogs.msdn.com/ericlippert/archive/2009/03/06/ - person Igal Tabachnik; 01.10.2009
comment
фатальные катастрофические невосстановимые исключения, такие как переход через ручьи. - person Phil Cooper; 07.03.2016

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

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

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

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

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

person Eric Lippert    schedule 27.02.2012
comment
Эта проблема довольно ортогональна блокировке IMO. Если вы получаете ожидаемое исключение, вы хотите очистить все, включая блокировки. И если вы получаете неожиданное исключение, у вас есть проблема, с блокировками или без них. - person CodesInChaos; 28.02.2012
comment
Я считаю, что описанная выше ситуация является обобщением. Иногда исключения описывают катастрофические события. Иногда они этого не делают. Каждый использует их в коде по-разному. Совершенно верно, что исключение является сигналом об исключительном, но не катастрофическом событии - при условии, что исключения = катастрофические, случай завершения процесса слишком конкретен. Тот факт, что это могло быть катастрофическим событием, не отменяет обоснованности вопроса - тот же ход мыслей может привести вас к тому, что вы никогда не обработаете какое-либо исключение, и в этом случае процесс будет завершен ... - person Gerasimos R; 31.10.2012
comment
@GerasimosR: Верно. Следует подчеркнуть два момента. Во-первых, исключения следует рассматривать как катастрофические до тех пор, пока они не будут признаны доброкачественными. Во-вторых, если вы получаете безопасное исключение, выброшенное из заблокированной области, то заблокированная область, вероятно, плохо спроектирована; вероятно, он слишком много работает внутри замка. - person Eric Lippert; 19.11.2012
comment
Я бы сказал, что правильнее всего в вашем сценарии было бы, чтобы код, который выходит из ванной после взрыва бомбы, помещал там знак неисправности. Может быть, просто оставить ванную комнату запертой, но это бесполезно. Возможно, имеет смысл выключить все, если кому-то нужно в туалет и он не может справиться с его отсутствием, но если у всех, кто стоял в очереди в ванную, есть стратегия, чтобы справиться с ее недоступностью [возможно, перейдите к еще один] нет причин, по которым жизнь не должна продолжаться. - person supercat; 01.04.2013
comment
При всем уважении, но похоже, что вы говорите, мистер Липперт, что дизайн C # блока блокировки, который под капотом использует оператор finally, - это плохо. Вместо этого мы должны полностью вывести из строя каждое приложение, которое когда-либо имело исключение в операторе блокировки, которое не было зафиксировано в блокировке. Может быть, вы не об этом говорите, но если это так, я действительно рад, что команда пошла тем путем, которым они пошли, а не вашим (экстремистским из соображений пуристов!) Путем. Всем уважении. - person Nicholas Petersen; 25.07.2016
comment
@NicholasPetersen: Вы утверждаете, что продолжать запускать программу с потенциально поврежденными внутренними структурами данных лучше, чем сбой или зависание. Включает ли ваша позиция молчаливое восстановление после разыменования нулевого указателя? Потому что прямо сейчас мы полностью разрушаем каждое приложение, в котором когда-либо было разыменование нулевого указателя. Вы недовольны тем, что C # приводит к сбою вашего приложения при разыменовании нулевого указателя по той пуристской причине, что такое приложение слишком глючно для жизни? - person Eric Lippert; 25.07.2016
comment
@NicholasPetersen: Ваша точная информация о моей позиции верна; Я действительно хочу, чтобы заданная блокировка на основе монитора и выдача исключения были на языке, чтобы необработанное исключение в блокировке должно было быть столь же фатальным, как и разыменование нуля , и отсутствие этого - недостаток. Но то, что там есть, важно. Моя более крупная позиция - это блокировка на основе монитора и выдача исключений, которые никогда не должны были одновременно помещаться в одну и ту же среду выполнения. Мониторы - не лучшее решение проблемы изменения общего состояния в системе с несколькими потоками управления. - person Eric Lippert; 25.07.2016
comment
@EricLippert спасибо за ваши ответы. Я уважаю вас, ребята низкого уровня, которые сделали .NET тем, чем он является. Ваши знания низкого уровня означают, что вы, ребята, несете на себе бремя понимания, которое часто не бывает у остальных. Тем не менее, я чувствую, что то, о чем вы говорите, сделало бы блокировки в C # чем-то опасным и ненавистным. Тогда это ужасно повлияет на удобство использования этого прекрасного языка. Я думаю, что решение состоит в том, чтобы позволить пользователю самим нести ответственность за то, как это работает. В критических случаях, когда блокировка заключает в себе очень плохие потенциалы, позвольте им самим поставить пробную ловушку в блокировку. - person Nicholas Petersen; 26.07.2016
comment
В голову приходит мысль, что, если бы блок catch мог следовать за блокировкой, поэтому вместо try-catch это был бы lock-catch (внутренне, конечно, это все еще просто попытка с помощью Monitor): lock (obj) {} catch (Exception ex) {...} И тогда ловушка все равно будет до finally (где блокировка потеряна). По крайней мере, это делает некоторые проблемы явными, тогда пользователь сможет справиться с тем фактом, что блокировка вот-вот будет потеряна. Просто подумай вслух, я не знаю. - person Nicholas Petersen; 26.07.2016
comment
@NicholasPetersen: Во-первых, да, я боюсь и ненавижу замки! :-) Программирование - это акт компоновки правильных решений небольших проблем в правильные решения больших проблем, но код, содержащий блокировки, несовместим. Блокировки активно работают против той самой особенности языка, которая делает его пригодным для использования! Теперь, с учетом сказанного, если у нас будут блокировки и исключения на одном языке, и если оператор блокировки является сахаром для попытки-наконец-то, тогда да, мне очень нравится ваша идея создания блока catch. Хорошая идея! - person Eric Lippert; 26.07.2016
comment
В случае, если неясно, что я имею в виду под несоставным: предположим, у нас есть передача метода, которая принимает два списка, s и d, блокирует s, блокирует d, удаляет элемент из s, добавляет элемент в d, разблокирует d, разблокирует s. Этот метод верен только , если никто никогда не пытается перейти из списка X в список Y одновременно с тем, как кто-то другой пытается перейти из Y в X. Правильность метода передачи не позволяет вам построить на его основе правильное решение более крупной проблемы, потому что блокировки представляют собой небезопасные мутации глобального состояния. Для безопасной передачи вы должны знать о каждой блокировке в программе. - person Eric Lippert; 26.07.2016
comment
Спасибо за ваш вклад, Эрик. Я рада, что тебе понравилась одна из моих идей. Одна вещь, которую я сейчас ценю лучше, - это то, что вам не нужна эта функция вообще. Я презираю вещи, которые наугад, в полном смысле этого слова, прерывают процесс приложения (это также происходит, я думаю, с необработанными исключениями в задачах, я не уверен в деталях, и я считаю, что сейчас есть обходной путь). Но что я могу оценить, так это то, что вы были не очень довольны завершением процесса, а скорее, что вам вообще не понравилась эта функция, и в этом случае не было бы необходимости прекращать процесс. - person Nicholas Petersen; 26.07.2016

да, это выпустит правильно; lock действует как _2 _ / _ 3_, с Monitor.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);
    }
} 
person Marc Gravell    schedule 27.02.2012

Оператор блокировки компилируется для вызова Monitor.Enter, а затем для блока try… finally. В блоке finally вызывается Monitor.Exit.

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

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

person GH.    schedule 01.10.2009
comment
Есть по крайней мере один случай, когда это неверно: прерывание потока в режиме отладки в версиях .net до 4. Причина в том, что компилятор C # вставляет NOP между Monitor.Enter и try, так что сразу следует за условием JIT нарушается. - person CodesInChaos; 28.02.2012

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

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

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

person Ry-♦    schedule 27.02.2012
comment
На самом деле, выполнение кода без гарантировано (например, можно потянуть за кабель питания), и это не совсем, как выглядит блокировка в 4.0 - см. здесь - person Marc Gravell; 27.02.2012
comment
@MarcGravell: Я подумал о том, чтобы поставить две сноски об этих двух точках. А потом я подумал, что это не имеет большого значения :) - person Ry-♦; 27.02.2012
comment
@MarcGravel: Я думаю, что все думают, что всегда речь не идет о ситуации «вытащить вилку», потому что программист не может это контролировать :) - person Vort3x; 27.02.2012

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

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

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

person Brian Rasmussen    schedule 26.02.2009