← Назад к вопросам

Что передаётся в lock?

2.0 Middle🔥 92 комментариев
#Асинхронность и многопоточность

Комментарии (2)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что передаётся в оператор lock?

В оператор lock в C# передаётся ссылочный объект, который используется в качестве примитива синхронизации (монитора) для обеспечения потокобезопасного доступа к общему ресурсу.

Ключевые принципы:

  1. Объект-маркер (token object):
    • Это должен быть ссылочный тип (класс), а не значимый тип (структура).
    • Обычно используется закрытое статическое или экземплярное поле, специально созданное для синхронизации.
    • НЕ следует использовать:
     - `this` (может привести к взаимоблокировкам, если `this` публично доступен).
     - `Type` объекты (например, `typeof(MyClass)`), так как они относятся к домену приложения и могут вызвать проблемы.
     - строковые литералы из-за интернирования строк.
     - публичные объекты, к которым есть доступ извне.

  1. Как работает lock:
    • При входе в блок lock происходит захват монитора переданного объекта.
    • Пока один поток удерживает монитор, другие потоки блокируются при попытке войти в блок lock с тем же объектом-маркером.
    • При выходе из блока монитор освобождается.

Пример правильного использования:

public class ThreadSafeCounter
{
    private int _counter = 0;
    private readonly object _lockObject = new object(); // Специальный объект для синхронизации

    public void Increment()
    {
        lock (_lockObject) // Передаём _lockObject как маркер синхронизации
        {
            _counter++;
        }
    }

    public int GetValue()
    {
        lock (_lockObject)
        {
            return _counter;
        }
    }
}

Важные технические детали:

  • Семантика lock:
    Компилятор преобразует конструкцию lock в следующий код:
bool lockTaken = false;
object temp = _lockObject;
try
{
    System.Threading.Monitor.Enter(temp, ref lockTaken);
    // Тело lock-блока
}
finally
{
    if (lockTaken)
        System.Threading.Monitor.Exit(temp);
}
  • Приватность объекта:
    Объект должен быть приватным, чтобы внешний код не мог случайно его использовать для своей синхронизации, что может привести к взаимоблокировкам.

  • Производительность:
    lock использует Monitor.Enter/Exit, что достаточно эффективно для большинства сценариев. Для высоконагруженных секций можно рассмотреть SemaphoreSlim, ReaderWriterLockSlim или Mutex.

Распространённые антипаттерны:

// ❌ ОПАСНО: использование this
lock (this) { ... }

// ❌ ОПАСНО: использование строк
lock ("myLock") { ... } // Строки интернируются — один объект на всё приложение!

// ❌ ОПАСНО: использование Type
lock (typeof(MyClass)) { ... } // Синхронизация на уровне домена приложения

// ❌ НЕКОМПИЛИРУЕТСЯ: значимый тип
int syncValue = 0;
lock (syncValue) { ... } // Ошибка компиляции!

Рекомендации по выбору объекта:

  • Для синхронизации доступа к полям экземпляра создавайте приватный объект в каждом экземпляре класса.
  • Для статической синхронизации используйте приватный статический объект.
  • Используйте атрибут [MethodImpl(MethodImplOptions.Synchronized)] для синхронизации целых методов (но осторожно — внутри также используется lock(this) или lock(typeof(...))).

Итог: В lock передаётся ссылочный объект, который выступает уникальным маркером для синхронизации доступа к критической секции. Правильный выбор этого объекта напрямую влияет на безопасность и отсутствие взаимоблокировок в многопоточной среде.

Что передаётся в lock? | PrepBro