事件可以在多线程应用程序中安全使用吗 [英] Can events be used in a multi-threaded application safely

查看:85
本文介绍了事件可以在多线程应用程序中安全使用吗的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在下面的代码中,我有两个类,一个类在单独的线程中运行并触发事件,另一个类订阅此事件并从该事件接收数据.我基于Jon Skeet的文章 http://csharpindepth.com/Articles/Chapter2/Events.aspx

In the following code I have two classes, one that runs in a separate thread and fires events, the other that subscribes to this event and receives data from the event. The event code I have based off of Jon Skeet's article http://csharpindepth.com/Articles/Chapter2/Events.aspx

在本文中 http://www.codeproject.com/Articles/37474/Threadsafe -事件它表示...

In this article http://www.codeproject.com/Articles/37474/Threadsafe-Events it says...

由于这个原因,我建议使用与Jon Skeet在代表和事件"末尾推荐的方法相同的方法:不要那样做",即,不要以多线程方式使用事件.如果一个对象上存在一个事件,则只有一个线程应该能够订阅或取消订阅该事件,并且该线程将引发该事件.

For this reason, I recommend the same approach that Jon Skeet ends up recommending at the end of Delegates and Events: "don't do that", i.e., don't use events in a multithreaded fashion. If an event exists on an object, then only one thread should be able to subscribe to or unsubscribe from that event, and it's the same thread that will raise the event.

现在,显然我的设计打破了这一点,因为它在与预订线程不同的线程上触发了该事件.我该如何修改我的设计,使其遵循不以多线程方式使用事件的原则,或者这是不可能的?

Now obviously my design breaks that, in that it fires the event on a different thread to that it was subscribed on. How could I modify my design so that it adhears to the principle of not using events in a multi-threaded fashion or is this not possible ?

我想到的另一种方法是将回调方法作为委托传递给B类,然后调用它而不是调用事件?

The other way I thought of doing it was just to pass in my callback method as a delegate into class B and call that instead of calling the event?

我的杆末端可能完全错了,因此请您澄清一下.

I may have completely the wrong end of the stick, so any clarification would be appreciated.

注意:我知道.Net 4.0显然已经解决了这个问题,但是我仍然对如果有一种方法可以在.Net 4之前解决这一问题感兴趣

Note: I am aware that .Net 4.0 has apparently solved this issue, however I would still be interested in if there is a way to do it pre .Net 4

public delegate void MyDelegate(int a);

class A
{
    void main()
    {
        B bObject = new B();
        bObject.MyEvent += new MyDelegate(NiceMethod);
        bObject.Run();
    }   

    void NiceMethod(int a)
    {   
        Console.Writeline({0}, a);  
    }
}

class B
{
    readonly object eventLock = new object();

    MyDelegate myDel;

    public event MyDelegate MyEvent
    {   
        add
        {
            lock (eventLock)
            {
                myDel += value;
            }
        }
        remove
        {
            lock (eventLock)
            {
                myDel -= value;
            }
        }
    }   

    //Assume this runs in a new thread and calls back data using MyEvent
    //Have ommited thread code for simplicity
    public void Run()
    {
        for (int i = 0; i < 100; i++)
        {
            Thread.Sleep(1000);
            MyDelegate handler;
            lock (someEventLock)
            {
                handler = myDel;
            }
            if (handler != null)
            {
                handler (i);
            }
        }   
    }
}

推荐答案

引发事件或侦听来自不同线程的事件没有任何问题.侦听器负责处理从另一个线程调用的问题.正如Marc Gravell在他的评论中指出的那样,编译器生成的addremove实现实现了(并且一直支持)在不同线程的事件之间来回侦听器,以及从这些线程中删除侦听器.唯一的问题是以线程安全的方式引发事件,这可以通过与生成的addremove使用的自旋锁同步对事件的访问来完成:

There isn't anything wrong about raising events or listening to events from different threads. It is the responsibility of the listener to deal with being invoked from another thread. As Marc Gravell notes in his comment, adding and removing of listeners to and from events from different threads is (and always has been) supported by the compiler generated add and remove implementations. The only problem is to raise the event in a thread safe fashion, which can be done by synchronizing the access to event via the same kind of spinlock the generated add and remove are using:

class B 
{  
    public event MyDelegate MyEvent;

    protected OnMyEvent(int p_Arg)
    {
        // Delegates are immutable and add/remove default
        // implementations always generate a new instance of the 
        // delegate. Therefore, tTmp (if not null) can be safely invoked
        var tTmp = 
            System.Threading.Interlocked
            .CompareExchange(ref MyEvent, null, null);
        if (tTmp != null) {
            tTmp(p_Arg);
        }
    }

    //Assume this runs in a new thread and calls back data using MyEvent 
    //Have ommited thread code for simplicity 
    public void Run() 
    { 
        for (int i = 0; i < 100; i++) 
        { 
            OnMyEvent(i);
        }    
    } 
} 

唯一可能发生的事情是在从事件列表中删除之后,调用该侦听器.恕我直言,侦听器必须能够处理这种情况,因为它可以处理从单独的线程调用的蜂鸣...

The only thing that could happen is that the listener is invoked after it has been removed from the event list. IMHO, the listener must be able to deal with this situation as it deals with beeing invoked from separate threads...

这篇关于事件可以在多线程应用程序中安全使用吗的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆