C #: Потокобезопасные события - PullRequest
5 голосов
/ 24 июня 2009

Является ли реализация ниже поточно-ориентированной? Если не то, что мне не хватает? Должен ли я где-то иметь ключевые слова volatile? Или блокировка где-нибудь в методе OnProcessingCompleted? Если да, то где?

public abstract class ProcessBase : IProcess
{
    private readonly object completedEventLock = new object();

    private event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;

    event EventHandler<ProcessCompletedEventArgs> IProcess.ProcessCompleted
    {
        add
        {
            lock (completedEventLock)
                ProcessCompleted += value;
        }
        remove
        {
            lock (completedEventLock)
                ProcessCompleted -= value;
        }
    }

    protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
    {
        EventHandler<ProcessCompletedEventArgs> handler = ProcessCompleted;
        if (handler != null)
            handler(this, e);
    }
}

Примечание: Причина, по которой у меня есть закрытое событие и явный интерфейс, заключается в том, что это абстрактный базовый класс. И классы, которые его наследуют, не должны ничего делать с этим событием напрямую. Добавлена ​​оболочка класса, чтобы она была более понятной =)

Ответы [ 2 ]

5 голосов
/ 24 июня 2009

Вам нужно заблокировать и при получении обработчика, иначе у вас может не быть последнего значения:

protected void OnProcessingCompleted(ProcessCompletedEventArgs e)
{
    EventHandler<ProcessCompletedEventArgs> handler;
    lock (completedEventLock) 
    {
        handler = ProcessCompleted;
    }
    if (handler != null)
        handler(this, e);
}

Обратите внимание, что это не предотвращает состояние гонки, когда мы решили, что мы собираемся выполнить набор обработчиков, и затем один обработчик отписался. Он по-прежнему будет вызываться, потому что мы получили делегат многоадресной рассылки, содержащий его, в переменную handler.

Вы ничего не можете с этим поделать, кроме как заставить самого обработчик осознавать, что он больше не должен вызываться.

Возможно, лучше просто не попытаться сделать события потокобезопасными - указать, что подписка должна только изменить поток, который вызовет событие.

4 голосов
/ 24 июня 2009

Нет необходимости, чтобы частный ProcessCompleted член был event - это может быть просто поле: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - внутри класса он всегда идет прямо к полю, поэтому материал event все равно потерял.

Подход, который вы продемонстрировали с помощью явного объекта блокировки, не намного более поточно-ориентированный, чем простое событие в виде поля (т. Е. public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - единственное отличие в том, что вы не ' Блокировка «this» (что хорошо - вы должны в идеале избегать блокировки на this) .. Подход «переменная-обработчик» верен, но есть еще side- эффекты, о которых вы должны знать .

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