Классы Monitor, Mutex и ReaderWriterLock поддерживают блокировки, имеющие сходство с потоками. Класс ReaderWriterLockSlim позволяет вам выбирать, он имеет конструктор, который принимает значение LockRecursionPolicy. Использование LockRecursionPolicy.NoRecursion - это оптимизация, довольно большая, если ваша блокировка действительно детализирована.
Класс Semaphore - это класс синхронизации, который не имеет никакого сходства потоков. Этот код надежно блокируется:
class Tester {
private Semaphore sem = new Semaphore(1, 1);
public void TestLock() {
sem.WaitOne();
for (int i = 0; i < 10; i++) Deadlock(i);
sem.Release();
}
private void Deadlock(int i) {
if (!sem.WaitOne(100)) Console.WriteLine("deadlock!");
else {
sem.Release();
Console.WriteLine("No deadlock!");
}
}
}
Как правило, классы аффинной синхронизации потоков требуют двух потоков и двух блокировок для взаимоблокировки. Стандартный шаблон - для одного потока получить блокировки A и B, для другого - B и A. Порядок важен.
В программировании на .NET существуют менее очевидные сценарии взаимоблокировок, вызванные блокировками, которые вы не видите, потому что они встроены в код платформы .NET. Очень классический для BackgroundWorker. Вы можете написать код в потоке пользовательского интерфейса, который вращается в свойстве Busy, ожидая завершения BGW. Это всегда блокируется, когда BGW имеет обработчик события RunWorkerCompleted. Он не может работать, пока поток пользовательского интерфейса не будет свободен, свойство Busy BGW не будет ложным, пока не завершится работа обработчика событий.