Ответ
В оператор lock передаётся объект-маркер (монитор), который используется для синхронизации потоков при доступе к критической секции кода. Этот объект должен быть ссылочного типа.
Критически важные правила выбора объекта:
- Приватный и только для блокировок: Объект должен быть
private(илиprivate staticдля статических методов) и не использоваться для других целей. Это предотвращает внешние взаимоблокировки. - Ссылочный тип: Примитивные типы значений (
int,bool) не подходят. Используйте специально созданный экземплярobject. - Избегайте
this: Использованиеlock(this)— антипаттерн, так как публичный экземпляр класса может быть заблокирован извне. - Избегайте
Typeи строк:lock(typeof(MyClass))опасен из-за глобальности.lock("myLock")опасен из-за интернирования строк — одна литеральная строка во всем приложении будет одним объектом.
Правильный пример:
public class ThreadSafeCounter
{
private int _count = 0;
private readonly object _lockObject = new object(); // Специальный объект для блокировки
public void Increment()
{
lock (_lockObject) // _lockObject выступает маркером для входа в критическую секцию
{
_count++; // Эта операция теперь атомарна для всех потоков
}
}
public int GetCount()
{
lock (_lockObject)
{
return _count;
}
}
}
Альтернативы: Для более сложных сценариев рассмотрите Monitor (который используется внутри lock), Mutex (для межпроцессной синхронизации) или примитивы из пространства имен System.Threading (например, SemaphoreSlim, ReaderWriterLockSlim).