Возможны ли в данном случае взаимоблокировки потоков?
Если я не ставлю lock
на получение, при этом у меня есть lock
на изменение значения, в данном случае возможна взаимоблокировка или я ошибаюсь?
Всегда ли нужно ставить lock
как на получение, так и на изменение в ситуации с многопоточностью?
public class MyClass
{
private object _lock = new object();
private string _property;
public string Property
{
get
{
// Получение без блокировки
return _property;
}
set
{
lock (_lock)
{
// Изменение с блокировкой
_property = value;
}
}
}
}
Ответы (1 шт):
Для потокобезопасного измененения коллекций существуют потокобезопасные коллекции.
Но допустим речь не об этом, а об одном единственном свойстве. Как сказали в комментариях, присвоение ссылочного типа атомарно, а тип string
неизменяемый, поэтому здесь lock
не нужен в принципе.
Но представим что операция изменения свойства не является потокобезопасной, тогда здесь ошибка, вы не можете добиться потокобезопасности поместив в lock
только сеттер, так как если запись не является потокобезопасной, то возможно, что произойдёт небезопасное чтение посередине записи.
Поэтому геттер тоже должен быть под локом.
public string Property
{
get
{
lock (_lock)
{
// небезопасное чтение
}
}
set
{
lock (_lock)
{
// небезопасная запись
}
}
}
Вы правильно подозреваете, что когда изменение свойства не происходит, то читать можно многопоточно не блокируя потоки между собой. Для этого существует шаблон проектирования Reader-Writer-Lock. Он говорит о том, что пока записи нет, любое количество потоков может читать данные, а когда требуется запись, нужно прекратить все чтения и писать только одним потом одновременно.
private readonly ReaderWriterLockSlim _lock = new();
public string Property
{
get
{
_lock.EnterReadLock();
try
{
// небезопасное чтение
}
finally
{
_lock.ExitReadLock();
}
}
set
{
_lock.EnterWriteLock();
try
{
// небезопасная запись
}
finally
{
_lock.ExitWriteLock();
}
}
}
В этом случае когда нет записи, блокировок при чтении вообще не будет. Само собой, что если есть гарантия, что операция чтения/записи никогда не выбросит исключение, то оборачивать её в try-finally
не требуется.