Страницы

Поиск по вопросам

пятница, 19 октября 2018 г.

Зачем нужен отдельный объект для блокировки?

Есть код
public class MyClass { private List nums; private object _syncRoot = new object();
public void Add1(int item) { lock(_syncRoot) { nums.Add(item); } }
public void Add2(int item) { lock(nums){ nums.Add(item); } } }
Почему правильно Add1 а не Add2


Ответ

В рамках вашего примера, формально оба варианта равноценны (лишь бы они не применялись одновременно). Вариант с отдельным приватным объектом является более идиоматичным, т. к. формально говоря, нет гарантии что кто-то ещё каким-то необычным образом не лочит List (например, конструктор самого List).
Разница будет в случае, когда nums может «утечь» наружу (например, вы выдаёте его через какой-то метод). В этом случае внешний код может для своих внутренних целей сделать lock(nums), и под ним ожидать окончания операции, выполняющейся в другом потоке. Если эта операция попытается вызвать Add2, вы получите deadlock.
К сожалению, это не такой уж и невероятный вариант развития событий.
Например, часто применяется форма lock(this), в которой объект, по которому ведётся блокировка, очевидно доступен снаружи, и таким образом, любой код может (случайно) организовать deadlock.
Для случая приватного объекта, который нужен лишь для блокировки, не возникает соблазна вынести его наружу. Ну и семантически правильнее, когда каждый объект выполняет лишь одну функцию.
Поэтому пользуйтесь вариантом с отдельным объектом для блокировки. Так вы ограждаете себя от потенциальных ошибок и необходимости думать о том, можете ли вы сделать именно этот объект публичным.

Официальная майкрософтовская документация говорит:
В общем случае избегайте блокировок по публичному типу, или по объектам, не находящихся под полным вашем контролем. Распространённые конструкции наподобие lock (this), lock (typeof (MyType)), или lock ("myLock") нарушают этот принцип: lock (this) может привести к проблемам, если этот экземпляр может быть доступен внешнему коду. lock (typeof (MyType)) может привести к проблемам, если тип MyType объявлен как public lock("myLock") может привести к проблемам, потому что любой код в пределах вашего процесса, использующий ту же строку, получит ту же блокировку. Лучше всего объявите приватный объект специально для блокировки по нему. Если вам нужно защитить данные, общие для всех объектов данного класса, используйте приватный статический объект.

Комментариев нет:

Отправить комментарий