Ответ
Ключевое слово lock в C# — это простейший механизм синхронизации, обеспечивающий взаимное исключение (mutual exclusion) для критических секций кода в многопоточных приложениях. Он гарантирует, что только один поток в данный момент может выполнять блок кода, защищенный lock для конкретного объекта-блокировки.
Основная цель: предотвращение состояний гонки (race conditions), когда несколько потоков одновременно изменяют общий ресурс, приводя к недетерминированному и ошибочному поведению.
Синтаксис и пример:
private static readonly object _syncRoot = new object(); // Объект-блокировка
private static int _sharedCounter = 0;
public void IncrementSafely()
{
// Критическая секция. Только один поток за раз может войти сюда,
// используя _syncRoot в качестве ключа.
lock (_syncRoot)
{
_sharedCounter++; // Операция теперь атомарна для потоков
}
}
Важные правила и лучшие практики:
-
Выбор объекта для блокировки:
- Используйте отдельный, приватный,
readonlyобъект специально созданный для этой цели (обычноobject). - НЕ блокируйте:
this— внешний код может случайно использовать ваш экземпляр для блокировки, что приведет к deadlock.typeof(MyClass)или публичные строки — это глобальные объекты, блокировка которых может непреднамеренно синхронизировать несвязанные части приложения.string— из-за интернирования строк.
- Используйте отдельный, приватный,
-
Краткость критической секции:
- Код внутри
lockдолжен выполняться как можно быстрее. Никогда не выполняйте долгие операции (ввод-вывод, сетевые запросы) внутри блокировки.
- Код внутри
-
Альтернативы для сложных сценариев:
Monitor.TryEnter(object, TimeSpan)— для блокировки с таймаутом.SemaphoreSlim,Mutex,ReaderWriterLockSlim— для более сложных схем синхронизации (например, множественное чтение / эксклюзивная запись).Interlockedкласс — для атомарных операций над простыми типами (инкремент, сравнение с обменом).
-
Остерегайтесь взаимной блокировки (deadlock):
- Deadlock возникает, когда два или более потока бесконечно ждут друг друга, каждый удерживая блокировку, нужную другому.
- Стратегия предотвращения: всегда устанавливайте строгий и одинаковый порядок захвата нескольких блокировок во всем коде.
Что происходит под капотом?
Конструкция lock является синтаксическим сахаром для использования класса Monitor:
// Код: lock(_syncRoot) { _sharedCounter++; }
// Примерно эквивалентен:
object __lockObj = _syncRoot;
bool __lockWasTaken = false;
try
{
System.Threading.Monitor.Enter(__lockObj, ref __lockWasTaken);
_sharedCounter++;
}
finally
{
if (__lockWasTaken)
System.Threading.Monitor.Exit(__lockObj);
} Видео-ответы
▶
▶
▶
▶
▶
▶
▶