Блокировка на объекте - PullRequest
5 голосов
/ 05 июня 2010

Я часто вижу код, подобный тому, который показан здесь , т. Е. Где объект размещается и затем используется как «объект блокировки».

Мне кажется, что для этого можно использовать любой объект, включая само событие в качестве объекта блокировки. Зачем выделять новый объект, который ничего не делает? Насколько я понимаю, вызов lock () для объекта на самом деле не изменяет сам объект и не блокирует его от использования, он просто используется в качестве заполнителя для нескольких операторов блокировки для привязки.

public class Shape : IDrawingObject, IShape
{
    // Create an event for each interface event
    event EventHandler PreDrawEvent;
    event EventHandler PostDrawEvent;

    object objectLock = new Object();

    // Explicit interface implementation required.
    // Associate IDrawingObject's event with
    // PreDrawEvent
    event EventHandler IDrawingObject.OnDraw
    {
        add
        {
            lock (objectLock)
            {
                PreDrawEvent += value;
            }
        }
        remove
        {
            lock (objectLock)
            {
                PreDrawEvent -= value;
            }
        }
    }
}

Итак, мой вопрос, это действительно хорошая вещь?

Ответы [ 3 ]

7 голосов
/ 05 июня 2010

включая само событие

Нет, ты не можешь этого сделать. «Событие» - это на самом деле просто некоторые методы доступа. Предполагая, что вы имеете в виду делегата поддержки, это было бы очень плохо - делегаты неизменны: каждый раз, когда вы добавляете / удаляете подписчика, вы получаете другой делегат.

На самом деле, теперь компилятор 4.0 делает это с кодом без блокировки, используя Interlocked - возможно, стоит придерживаться этого подхода.

В вашем примере objectLock гарантирует, что все вызывающие (для этого экземпляра) блокируются на один и тот же объект, что важно, но без уродства блокировки на this (который является как компилятор C # использовал для работы).

-

Обновление: в вашем примере показан код, необходимый до C # 4.0, доступ к полевому событию внутри тип говорил непосредственно с полем: обычная блокировка полевого события не была соблюдена , Это было изменено в C # 4.0; теперь вы можете (в C # 4.0) безопасно переписать это как:

public class Shape : IDrawingObject, IShape
{
    // Create an event for each interface event
    event EventHandler PreDrawEvent;
    event EventHandler PostDrawEvent;

    event EventHandler IDrawingObject.OnDraw
    {
        add { PreDrawEvent += value; }
        remove { PreDrawEvent -= value; }
    }
}

Затем следует все правильное поведение.

2 голосов
/ 05 июня 2010

Любой член закрытого ссылочного типа выполнит эту работу. Пока это является частным и никогда не будет переназначено. Что выбивает объект делегата из работы, вы определенно не хотите, чтобы блокировка не выполняла свою работу просто потому, что клиентский код, которым вы не управляете, назначает обработчик событий. Чрезвычайно трудно отлаживать.

Использование частных членов, выполняющих другую работу, не очень хорошо масштабируется. Если вы обнаружите, что вам нужно заблокировать другой регион кода во время рефакторинга или отладки, вам нужно будет найти другого частного участника. Вот где все может быстро скиснуть: вы можете выбрать того же самого частного участника снова. Тупик стучит в дверь.

Этого не произойдет, если вы назначите объект блокировки определенному набору общих переменных, которые необходимо защитить. Позволяет также дать ему хорошее имя.

1 голос
/ 05 июня 2010

Рекомендуется блокировать частное статическое поле, поскольку это гарантирует, что несколько потоков, пытающихся получить доступ к блокировке параллельно, будут заблокированы. Блокировка экземпляра самого класса (lock(this)) или некоторого поля экземпляра может быть проблематичной, поскольку, если два потока вызывают метод для двух разных экземпляров объекта, они смогут одновременно ввести оператор блокировки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...