Ответ
Конструкция lock — это синтаксический сахар для работы с классом Monitor. Компилятор C# разворачивает lock в вызовы Monitor.Enter и Monitor.Exit внутри блока try-finally, что гарантирует освобождение блокировки даже при исключении.
Исходный код:
private readonly object _syncRoot = new object();
lock (_syncRoot)
{
// Критическая секция
sharedResource.Modify();
}
Во что это компилируется (примерный эквивалент):
bool lockTaken = false;
try
{
Monitor.Enter(_syncRoot, ref lockTaken);
// Критическая секция
sharedResource.Modify();
}
finally
{
if (lockTaken)
Monitor.Exit(_syncRoot);
}
Ключевые моменты:
- Переменная
lockTaken(передаваемая по ссылке) нужна для защиты от ситуации, когда исключение возникнет между попыткой захватаMonitor.Enterи установкой флага. Без неё вfinallyмог бы вызватьсяMonitor.Exitдля не захваченной блокировки, что привело бы кSynchronizationLockException. lockв C# реализует повторно входимую (reentrant) блокировку: поток, уже владеющий блокировкой, может снова войти вlockна том же объекте.- Для блокировки следует использовать отдельный private объект, а не
thisили публичные объекты, чтобы избежать случайных взаимоблокировок.
Важно: lock работает только в рамках одного процесса. Для межпроцессной синхронизации нужны Mutex или Semaphore.