Я обычно слышал, что это хорошая идея, чтобы разблокировать блокировки перед вызовом прослушивателей событий, чтобы избежать тупика. Однако, поскольку блок lock {}
является реентерабельным по тому же потоку в С#, нормально ли вызывать события из заблокированного блока или мне нужно сделать копию соответствующих данных состояния и вызвать событие вне блока блокировки?
Если нет, укажите пример, когда было бы проблемой вызвать событие из блока lock {}
.
Спасибо
Я не помню, чтобы мне приходилось поднимать событие из инструкции lock
. Но не трудно представить, что происходит плохо.
Когда вы поднимаете событие, вы откладываете исполнение на код, который может быть не вашим. Это особенно верно, если вы пишете какую-то библиотеку или фреймворк, например, которые будут использоваться другими. Внутри обработчика событий вы абсолютно не контролируете происходящее. Обработчик событий может запустить новый поток и дождаться окончания этого потока (т.е. Join()
) перед возвратом. Если этот новый поток называется некоторой функцией, которая заблокирована на той же переменной, что и ваш lock
, bingo. Тупик.
Но кроме того, наилучшей практикой является минимизация времени, которое вы тратите на lock
, чтобы уменьшить "блокировку" точки блокировки. Если вы поднимаете событие внутри lock
, все ставки отключены.
Проблема заключается не в том, что обработчик событий может попытаться позвонить в блокировку, которую у вас уже есть, проблема в том, что обработчик событий может попытаться получить какую-то другую блокировку (возможно, блокировку и настройку для тупика), или что обработчик события может запустить некоторую долгосрочную задачу, такую как запрос к базе данных (оставляя вашу блокировку недоступной для других потоков, пока она не завершится). Общее правило заключается в том, что вы должны держать блокировку как можно короче.
Смешивание потоков с обработчиками событий, которые вы не контролируете, безусловно, открывает вам проблемы. В настоящее время у меня проблемы, потому что я возбудил событие из потока последовательного порта. Некоторый код обработчика событий решил заблокировать и ждать, пока из последовательного порта не будет получено другое сообщение. Это будет долгое ожидание, потому что вы просто заблокировали один и единственный поток! Я даже не могу рассердиться на кого-либо, потому что написал оба фрагмента кода (через год, чтобы у меня было время забыть подробности).
Да, это не будет считаться хорошим дизайном для вызова события из заблокированного замка. В основном из-за перехода из заблокированного раздела, и если это должно было вызвать ошибку или возникнуть проблема, блокировка не обязательно будет выпущена.
В общем, вы хотите свести к минимуму время, в течение которого вы находитесь в замке, чтобы не допустить блокировки слишком долго (вызывая задержки), а также уменьшить вероятность того, что у вас будет код, который может выйти из строя во время блокировки.
lock
и он вызвал исключение, блокировка будет снята. Ключевое слово lock
в основном является сокращенной записью для System.Threading.Monitor.Enter/Exit
заключенной в блок try-finally. То есть метод Exit () вызывается из блока finally, поэтому блокировка будет снята.