C#:线程安全事件

时间:2009-06-24 11:36:12

标签: c# multithreading events event-handling thread-safety

下面的实现是否是线程安全的?如果不是我错过了什么?我应该在某个地方拥有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 个答案:

答案 0 :(得分:5)

您在获取处理程序时也需要锁定,否则您可能没有最新值:

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

请注意,此不会阻止竞争条件,我们决定执行一组处理程序,然后然后一个处理程序取消订阅。它仍然会被调用,因为我们已经将包含它的多播委托提取到handler变量中。

除了让处理程序本身意识到它不应再被调用之外,你可以做很多事情。

最好不要尝试使事件成为线程安全的 - 指定订阅只应 更改将引发事件的线程。

答案 1 :(得分:4)

私人ProcessCompleted成员不需要成为event - 它可能只是一个字段:private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - 在课堂内它总是直接进入字段,所以无论如何,event的东西都会丢失。

您使用显式锁定对象显示的方法并不比仅具有类似字段的事件(即public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted;更加 更多线程安全 - 唯一的区别是你没有锁定“这个”(这是一件好事 - 你应该理想避免锁定this)..“处理程序变量”方法是正确的,但有仍然是side-effects you should be aware of