C# 如何让一个类的多个实例(在不同的线程中)侦听同一事件?

C# 如何让一个类的多个实例(在不同的线程中)侦听同一事件?,c#,multithreading,events,C#,Multithreading,Events,我不熟悉C#中的事件处理和线程,所以如果这个问题是基本问题,请原谅我:如何创建几个类在不同线程上运行,所有类都侦听同一事件 例如,我经常收到一条新数据,但间隔是随机的。当这些数据到达时,我用新数据更新一个类,我们称之为MyDataClass,它会引发一个事件:MyDataClass.NewEvent 然后我有一个名为NewEventHandler的类。当触发事件时,此类使用新数据进行一些计算,然后将结果发送到另一个应用程序 所以问题是: 我需要大约30个NewEventHandler的实例,所有

我不熟悉C#中的事件处理和线程,所以如果这个问题是基本问题,请原谅我:如何创建几个类在不同线程上运行,所有类都侦听同一事件

例如,我经常收到一条新数据,但间隔是随机的。当这些数据到达时,我用新数据更新一个类,我们称之为
MyDataClass
,它会引发一个事件:
MyDataClass.NewEvent

然后我有一个名为
NewEventHandler
的类。当触发事件时,此类使用新数据进行一些计算,然后将结果发送到另一个应用程序

所以问题是: 我需要大约30个
NewEventHandler
的实例,所有这些实例都在监听
MyDataClass.NewEvent
(每个实例进行不同的计算并产生不同的结果)。重要的是,这些计算必须同时运行—只要事件触发所有30个
NewEventHandler
实例,就开始计算。但是,它们是否一起完成并不重要(例如,这里不需要同步)

如何实际创建
NewEventHandler
的实例,以便它们在不同的线程上运行,并使它们都侦听
MyDataClass.NewEvent
的单个实例?

在C#中,一般做法是在触发事件的同一线程上调用事件侦听器的方法。从源类触发事件的标准模板是:

void FireNewEvent() 
{
     var tmp = NewEvent;
     if( tmp != null )
     { 
         tmp(this, YourEventArgsInstance);
     }
}
事件并不多,只是一个小小的光荣代表。因此,此调用类似于多播委托调用-这意味着所有订阅者将在运行
FireNewEvent
的同一线程上顺序调用。我建议你不要改变这种行为

如果要同时运行事件订阅服务器,则在每个订阅服务器中启动一个新任务

...
MyDataClass.NewEvent += OneOfSubscriberClassInstance.OnNewEvent;

...
public void OnNewEvent(object sender, YourEventArgs args)
{ 
   Task.Factory.StartNew( () => {

       // all your event handling code here 
   });
}

触发事件的代码将按顺序触发30个订阅服务器,但每个订阅服务器将在TPL安排的自己的线程中运行。因此,触发事件的代理不必等到当前调用的订阅服务器处理程序处理完事件后才触发下一个订阅服务器处理程序。

下面是一个示例/演示,说明如何同步不同的线程并确保它们都同时响应事件。您可以将此代码复制并粘贴到控制台应用程序中,以查看其运行

public class Program
{
    private static EventWaitHandle _waitHandle;
    private const int ThreadCount = 20;
    private static int _signalledCount = 0;
    private static int _invokedCount = 0;
    private static int _eventCapturedCount = 0;
    private static CountdownEvent _startCounter;
    private static CountdownEvent _invokeCounter;
    private static CountdownEvent _eventCaptured;


    public static void Main(string[] args)
    {
        _waitHandle = new EventWaitHandle(false, EventResetMode.ManualReset);
        _startCounter = new CountdownEvent(ThreadCount);
        _invokeCounter = new CountdownEvent(ThreadCount);
        _eventCaptured = new CountdownEvent(ThreadCount);

        //Start multiple threads that block until signalled
        for (int i = 1; i <= ThreadCount; i++)
        {
            var t = new Thread(new ParameterizedThreadStart(ThreadProc));
            t.Start(i);
        }

        //Allow all threads to start
        Thread.Sleep(100);

        _startCounter.Wait();

        Console.WriteLine("Press ENTER to allow waiting threads to proceed.");
        Console.ReadLine();

        //Signal threads to start
        _waitHandle.Set();

        //Wait for all threads to acknowledge start
        _invokeCounter.Wait();

        //Signal threads to proceed
        _waitHandle.Reset();

        Console.WriteLine("All threads ready. Raising event.");

        var me = new object();

        //Raise the event
        MyEvent(me, new EventArgs());

        //Wait for all threads to capture event
        _eventCaptured.Wait();
        Console.WriteLine("{0} of {1} threads responded to event.", _eventCapturedCount, ThreadCount);
        Console.ReadLine();
    }

    public static EventHandler MyEvent;

    public static void ThreadProc(object index)
    {
        //Signal main thread that this thread has started
        _startCounter.Signal();
        Interlocked.Increment(ref _signalledCount);

        //Subscribe to event
        MyEvent += delegate(object sender, EventArgs args) 
        { 
            Console.WriteLine("Thread {0} responded to event.", index);
            _eventCaptured.Signal();
            Interlocked.Increment(ref _eventCapturedCount);
        };

        Console.WriteLine("Thread {0} blocks.", index);

        //Wait for main thread to signal ok to start
        _waitHandle.WaitOne();

        //Signal main thread that this thread has been invoked
        _invokeCounter.Signal();
        Interlocked.Increment(ref _invokedCount);
    }
}
公共类程序
{
私有静态EventWaitHandle\u waitHandle;
private const int ThreadCount=20;
专用静态int_signaledcount=0;
私有静态int_invokedCount=0;
私有静态int_eventCapturedCount=0;
私有静态倒计时事件_startCounter;
私有静态倒计时事件_invokeCounter;
私有静态倒计时事件_eventCaptured;
公共静态void Main(字符串[]args)
{
_waitHandle=新的EventWaitHandle(错误,EventResetMode.ManualReset);
_startCounter=新的CountdownEvent(线程计数);
_invokeCounter=新的CountdownEvent(线程计数);
_eventCaptured=新的CountdownEvent(线程计数);
//启动阻塞的多个线程,直到发出信号

对于(int i=1;我可能重复它可能是重复的,但我喜欢这个问题的抽象性质。“等待一个事件的多线程?”是关于OP域问题的具体解读。我希望这个问题能够自己得到回答。@Eric问题不同-这个问题是关于处理C#风格的事件(委托),建议的方法是关于阻止操作系统事件上的同步。请注意,类的实例不会“在不同的线程上运行”,它们根本不会运行。只有特定的方法(或者在
async
/
wait
code的情况下,甚至是方法的一部分)可以说是“在线程上运行”。我要指出,事件(正如问题所提出的那样)实际上是一个委托,该委托是在触发它的同一线程上调用的,因此另一个线程不会真正侦听事件。最简单的方法是设置ManualResetEvent,而不是触发C#事件。+1看起来MyDataClass应该有一些方法(用于任务)这开始了一个新的线程。谢谢-我以前没有看过任务库,看起来它做得很好!提出了一整套新问题,但对于任何阅读了相同问题的人来说,它看起来冗长且易于理解