c# - 事件可以安全地用于多线程应用程序吗

标签 c# multithreading events

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

在这篇文章中http://www.codeproject.com/Articles/37474/Threadsafe-Events它说...

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.

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

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

我的想法可能完全错误,所以任何澄清都将不胜感激。

注意:我知道 .Net 4.0 显然已经解决了这个问题,但是我仍然想知道是否有办法在 .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 相同类型的自旋锁同步对事件的访问来完成使用:

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

唯一可能发生的事情是监听器被调用之后它已从事件列表中删除。恕我直言,监听器必须能够处理这种情况,因为它处理从不同线程调用的 beeing...

关于c# - 事件可以安全地用于多线程应用程序吗,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11659578/

相关文章:

c# - 如何增加 richtextbox 中文本的字体大小

c# - 为什么 TargetNullValue 更新可为空的 Source

python - 尝试将图像从 QThread 发送到 PyQt 中的主线程或其他线程

.net - 是否有围绕任务并行库的接口(interface)包装器,以便我可以将其换出进行单元测试?

android - 检查事件是否已经在日历中

c# - 在 LINQ to SQL C# 中选择计数

c# - sql语法问题或者代码问题? "Index was outside the bounds of the array."

python - 多人游戏中的Pygame和socket : OverflowError

events - Github 上的 Pull Request Merge 向我的应用程序发送 POST 请求

javascript - HTML 的 'onclick' 是否适用于移动设备?