Ответ 1
Это не так много, что полезно сделать это, поскольку это полезно для того, чтобы им было позволено. Подумайте, как часто вы можете использовать общедоступные методы, которые вызывают другие общедоступные методы. Если публичный метод вызывается в блокировках, а вызов public метода в него должен блокировать более широкую область его действия, то возможность использования рекурсивных блокировок означает, что вы можете это сделать.
Есть некоторые случаи, когда вам может показаться, что вы используете два объекта блокировки, но вы собираетесь использовать их вместе, и, следовательно, если вы допустили ошибку, существует большой риск тупика. Если вы можете иметь дело с более широким охватом, предоставляемым блокировке, то использование одного и того же объекта для обоих случаев - и рекурсия в тех случаях, когда вы будете использовать оба объекта, - удалит эти особые взаимоблокировки.
Однако...
Эта полезность является дискуссионным.
В первом случае я приведу из Joe Duffy:
Рекурсия обычно указывает на чрезмерное упрощение в вашей конструкции синхронизации, что часто приводит к менее надежному коду. В некоторых проектах используется рекурсия блокировки как способ избежать разделения функций на те, которые занимают блокировки, и те, которые предполагают, что блокировки уже приняты. Это может, по общему признанию, привести к уменьшению размера кода и, следовательно, сокращению времени записи, но в итоге получается более хрупкий дизайн. Всегда лучше использовать код для публичных точек входа, в которых используются нерекурсивные блокировки, а внутренние функции рабочих, которые утверждают блокировку, сохраняются. Рекурсивные вызовы блокировки - это избыточная работа, которая вносит вклад в необработанные служебные данные. Но хуже, в зависимости от рекурсии, может усложнить понимание поведения синхронизации вашей программы, в частности, на каких границах предполагается использовать инварианты. Обычно wed хотел сказать, что первая строка после захвата блокировки представляет собой инвариантную "безопасную точку" для объекта, но как только будет введена рекурсия, это утверждение больше нельзя сделать уверенным. Это, в свою очередь, затрудняет правильное и надежное поведение при динамической компоновке.
(Джо больше говорит по этому поводу в другом месте своего блога, а в свою книгу о параллельном программировании).
Второй случай уравновешивается случаями, когда рекурсивная запись блокировки приводит к возникновению разных типов тупика или увеличивает уровень конкуренции настолько высок, что могут быть также взаимоблокировки (Этот парень говорит, что он предпочел бы, чтобы он просто зашел в тупик при первом повторении, я не согласен - я бы предпочел просто просто выбросить большое исключение, которое привело мое приложение к хорошая трассировка стека).
Одна из худших вещей заключается в том, что она упрощается в неподходящее время: когда вы пишете код, проще использовать рекурсию блокировки, чем расщеплять больше, а глубже думать о том, что нужно блокировать. Однако, когда вы отлаживаете код, тот факт, что оставлять блокировку не означает, что это блокирование усложняет ситуацию. Какая неудача - когда мы думаем, что знаем, что мы делаем, сложный код - это соблазн, которым можно наслаждаться в свое время, чтобы вы не занимались часами, и когда мы поняли, что мы испортили это мы больше всего хотим, чтобы все было красивым и простым.
Вы действительно не хотите смешивать их с переменными состояния.
Привет, POSIX-потоки только из-за смелости
По крайней мере ключевое слово lock
означает, что мы избегаем возможности сопоставления Monitor.Exit()
для каждого Monitor.Enter()
, что делает некоторые из рисков менее вероятными. До тех пор, пока вам не нужно что-то делать за пределами этой модели.
С более поздними классами блокировки .NET делает это, чтобы помочь людям избежать использования блокировки-рекурсии, не блокируя тех, кто использует старые шаблоны кодирования. ReaderWriterLockSlim
имеет перегрузку конструктора, которая позволяет вам использовать рекурсию, но по умолчанию это LockRecursionPolicy.NoRecursion
.
Часто при решении вопросов concurrency мы должны принять решение между более сложным методом, который потенциально может дать нам лучше concurrency, но который требует гораздо большей осторожности, чтобы быть уверенным в правильности и более простой технике, которая могла бы потенциально давайте хуже concurrency, но где легче быть уверенным в правильности. Используя рекурсивные блокировки, мы получаем технику, в которой мы будем удерживать блокировки дольше и иметь менее хороший concurrency, а также быть менее уверенными в правильности и более сложной отладке.