简体   繁体   English

C#:线程安全事件

[英]C#: Thread-safe events

Is the implementation below thread-safe? 下面的实现是否是线程安全的? If not what am I missing? 如果不是我错过了什么? Should I have the volatile keywords somewhere? 我应该在某个地方使用volatile关键字吗? Or a lock somewhere in the OnProcessingCompleted method? 或者OnProcessingCompleted方法中的某个锁? If so, where? 如果是的话,在哪里?

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);
    }
}

Note: The reason why I have private event and explicit interface stuff, is because it is an abstract base class. 注意: 我有私有事件和显式接口的原因是因为它是一个抽象基类。 And the classes that inherit from it shouldn't do anything with that event directly. 从它继承的类不应该直接对该事件做任何事情。 Added the class wrapper so that it is more clear =) 添加了类包装器,使其更清晰=)

You need to lock when you fetch the handler too, otherwise you may not have the latest value: 您也需要在获取处理程序时锁定,否则您可能没有最新值:

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

Note that this doesn't prevent a race condition where we've decided we're going to execute a set of handlers and then one handler unsubscribed. 请注意,这不会阻止竞争条件,我们已经决定执行一组处理程序, 然后取消订阅一个处理程序。 It will still be called, because we've fetched the multicast delegate containing it into the handler variable. 它仍然会被调用,因为我们已经将包含它的多播委托提取到handler变量中。

There's not a lot you can do about this, other than making the handler itself aware that it shouldn't be called any more. 除了让处理程序本身意识到它不应再被调用之外,你可以做很多事情。

It's arguably better to just not try to make the events thread-safe - specify that the subscription should only change in the thread which will raise the event. 最好不要尝试使事件成为线程安全的 - 指定订阅应在引发事件的线程中更改。

There is no need for the private ProcessCompleted member to be an event - it could just be a field: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; 私有ProcessCompleted成员不需要成为一个event - 它可能只是一个字段: private EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - inside the class it always goes straight to the field, so the event stuff is lost anyway. - 在课堂内,它总是直接进入战场,所以无论如何event都会丢失。

The approach you've shown with an explicit lock object isn't much more thread-safe than just having a field-like event (ie public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; - the only difference is that you aren't locking "this" (which is a good thing - you should ideally avoid locking on this ).. The "handler variable" approach is the right one, but there are still side-effects you should be aware of . 你已经有一个明确的锁定对象显示的方法是没有更多的线程安全不仅仅是一个具有字段类事件(即public event EventHandler<ProcessCompletedEventArgs> ProcessCompleted; -唯一的区别是,你是不是锁定“这个“(这是一件好事 - 理想情况下你应该避免this锁定)。”处理程序变量“方法是正确的,但仍然存在应该注意的副作用

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM