Возможный дубликат:
Заблокированные блокировки на С#
Почему этот код не вызывает тупик?
private static readonly object a = new object();
...
lock(a)
{
lock(a)
{
....
}
}
Возможный дубликат:
Заблокированные блокировки на С#
Почему этот код не вызывает тупик?
private static readonly object a = new object();
...
lock(a)
{
lock(a)
{
....
}
}
Если поток уже содержит блокировку, то он может "принять эту блокировку" снова без проблем.
Что касается того, почему это (и почему это хорошая идея), рассмотрим следующую ситуацию, когда у нас есть определенный порядок блокировки в другом месте программы a → b:
void f()
{
lock(a)
{ /* do stuff inside a */ }
}
void doStuff()
{
lock(b)
{
//do stuff inside b, that involves leaving b in an inconsistent state
f();
//do more stuff inside b so that its consistent again
}
}
Упс, мы просто нарушили порядок блокировки и имели потенциальный тупик в наших руках.
Нам действительно нужно иметь возможность сделать следующее:
function doStuff()
{
lock(a)
lock(b)
{
//do stuff inside b, that involves leaving b in an inconsistent state
f();
//do more stuff inside b so that its consistent again
}
}
Итак, наше упорядочение блокировки поддерживается без самозатухающего при вызове f()
.
Ключевое слово lock
использует блокировку повторного входа, то есть текущий поток уже имеет блокировку, поэтому он не пытается ее восстановить.
Тупик возникает, если
Тема 1 получает блокировку A
Тема 2 получает блокировку B
Thread 1 пытается получить блокировку B (ждет выполнения Thread 2)
Thread 2 пытается получить блокировку A (ждет завершения Thread 1)
Оба потока теперь ждут друг друга и, таким образом, блокируются.
Из раздела 8.12 спецификации языка С#:
В то время как блокировка взаимного исключения сохраняется, выполнение кода в одном и том же исполнении поток также может получать и освобождать замок. Однако код, выполняемый в других потоки заблокированы от получения блокировки до тех пор, пока блокировка не будет отпущена.
Очевидно, что внутренняя область lock
находится в том же потоке, что и внешний.