C #: вызвать событие из заблокированного блока

2

Я обычно слышал, что это хорошая идея, чтобы разблокировать блокировки перед вызовом прослушивателей событий, чтобы избежать тупика. Однако, поскольку блок lock {} является реентерабельным по тому же потоку в С#, нормально ли вызывать события из заблокированного блока или мне нужно сделать копию соответствующих данных состояния и вызвать событие вне блока блокировки?

Если нет, укажите пример, когда было бы проблемой вызвать событие из блока lock {}.

Спасибо

Теги:
multithreading
events
deadlock
locking

3 ответа

9
Лучший ответ

Я не помню, чтобы мне приходилось поднимать событие из инструкции lock. Но не трудно представить, что происходит плохо.

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

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

  • 0
    Класс System.IO.Ports.SerialPort довольно прост в использовании - я подключился и работал очень быстро, потому что никогда не видел его раньше. Мне не хватало некоторых байтов из-за свойства DiscardNull, и я потратил некоторое время, чтобы понять, что событие DataReceived может не вызываться, даже если там ожидают какие-то данные.
  • 0
    @ Кортни: Спасибо!
Показать ещё 1 комментарий
4

Проблема заключается не в том, что обработчик событий может попытаться позвонить в блокировку, которую у вас уже есть, проблема в том, что обработчик событий может попытаться получить какую-то другую блокировку (возможно, блокировку и настройку для тупика), или что обработчик события может запустить некоторую долгосрочную задачу, такую ​​как запрос к базе данных (оставляя вашу блокировку недоступной для других потоков, пока она не завершится). Общее правило заключается в том, что вы должны держать блокировку как можно короче.

Смешивание потоков с обработчиками событий, которые вы не контролируете, безусловно, открывает вам проблемы. В настоящее время у меня проблемы, потому что я возбудил событие из потока последовательного порта. Некоторый код обработчика событий решил заблокировать и ждать, пока из последовательного порта не будет получено другое сообщение. Это будет долгое ожидание, потому что вы просто заблокировали один и единственный поток! Я даже не могу рассердиться на кого-либо, потому что написал оба фрагмента кода (через год, чтобы у меня было время забыть подробности).

  • 0
    @ Дон Киркби: Совершенно отдельный вопрос. Вы упомянули «последовательный порт». Вы делаете это в C #? Любые хорошие веб-сайты, справочные материалы, книги и т. Д., Чтобы посмотреть? Я собираюсь заняться этим в ближайшие несколько месяцев. Любая помощь будет оценена. Извините, что похитил эту тему.
  • 0
    @Matt: это не сложно. Просто напишите строку или байтовый массив, а затем прочитайте ответ или зарегистрируйтесь для события DataReceived. Я не думаю, что вам действительно нужно больше, чем руководство: msdn.microsoft.com/en-us/library/…
1

Да, это не будет считаться хорошим дизайном для вызова события из заблокированного замка. В основном из-за перехода из заблокированного раздела, и если это должно было вызвать ошибку или возникнуть проблема, блокировка не обязательно будет выпущена.

В общем, вы хотите свести к минимуму время, в течение которого вы находитесь в замке, чтобы не допустить блокировки слишком долго (вызывая задержки), а также уменьшить вероятность того, что у вас будет код, который может выйти из строя во время блокировки.

  • 1
    Если обработчик события был вызван внутри lock и он вызвал исключение, блокировка будет снята. Ключевое слово lock в основном является сокращенной записью для System.Threading.Monitor.Enter/Exit заключенной в блок try-finally. То есть метод Exit () вызывается из блока finally, поэтому блокировка будет снята.
  • 0
    На самом деле это не обязательно так. Если исключение распространялось на вызывающего абонента, которому принадлежит блокировка yes, оно будет снято. Если исключение произошло в другом потоке и оставило вызывающую сторону в заблокированном состоянии (то есть ожидает возврата), то этого не произойдет.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню