强制事件处理程序在对象';s线程,C#NET

强制事件处理程序在对象';s线程,C#NET,c#,multithreading,C#,Multithreading,我有一个类处理由多个成员对象创建的事件。来自这些对象的事件为事件生成工作线程,因此我的类中的各种事件处理程序都在不同的线程上运行(一个是串行处理程序,一个是计时器事件,等等)。我正在寻找一种使代码线程安全的简单方法,最好是强制事件处理程序在我的对象线程上运行 如果这是一个Forms UI对象,我可以利用其ISynchronizeInvoke接口的实现,调用Invokererequired,Invoke,等等。在WPF中,我可以使用Dispatcher对象。但是我的类需要独立于任何UI代码运行 以

我有一个类处理由多个成员对象创建的事件。来自这些对象的事件为事件生成工作线程,因此我的类中的各种事件处理程序都在不同的线程上运行(一个是串行处理程序,一个是计时器事件,等等)。我正在寻找一种使代码线程安全的简单方法,最好是强制事件处理程序在我的对象线程上运行

如果这是一个Forms UI对象,我可以利用其
ISynchronizeInvoke
接口的实现,调用
Invokererequired
Invoke
,等等。在WPF中,我可以使用
Dispatcher
对象。但是我的类需要独立于任何UI代码运行

以下是我所拥有的一个简化示例:

public class MyClass
{
    private SomeObject object1;
    private AnotherObject object2;

    public MyClass()
    {
        object1 = new SomeObject();
        object2 = new AnotherObject();

        object1.AThreadedEvent += ThreadedEventHandler1;
        object2.AnotherThreadedEvent += ThreadedEventHandler2;
    }

    // This runs in its own thread!
    private void ThreadedEventHandler1()
    {
        // DO STUFF HERE
    }

    // This runs in its own thread!
    private void ThreadedEventHandler2()
    {
        // DO STUFF HERE
    }
}
由于两个事件处理程序都访问父类中的相同对象(包括彼此!),因此如果有一种简单的方法强制事件处理程序在创建对象的线程中运行,那就太棒了

我曾想过让我的类实现
ISynchronizeInvoke
接口,但这样做似乎会变得相当复杂。在我跳进兔子洞之前,我想我应该找专家看看是否有更简单的解决办法

想法

编辑:


我想在父对象的线程中运行事件处理程序的部分原因是因为父对象有自己的*事件,这些事件是根据其成员对象发送的事件触发的。我希望任何线程功能都被这个类隐藏,这样使用这个类的代码就不必担心与线程相关的问题(如锁等)。简单地锁定共享数据不会起作用,因为我*仍然需要从线程化事件处理程序中触发事件。

在另一个线程上调用的想法是通过一个while循环来实现的,该循环会不时检查是否有“外部”消息要处理。对于UI,有一个windows循环可以实现这一点。对于外部线程,必须手动写入循环。想象一个没有循环的情况,你有一个相对长时间运行的线程,对吗?突然,你想中断这个线程来调用你的消息,并恢复它在同一个共享堆栈内存上所做的事情。此中断将破坏堆栈。这根本不可能。另一种可能是使用ManualResetEvent之类的同步机制,只需等待一个信号(来自线程外部的信号)。因此,要继续,为了处理来自另一个线程的消息,基本上只有两个选项:

1) 您有一个while循环,最终使用一点睡眠(给其他线程一些时间/滴答声来完成它们的工作)

2) 您只需等待消息以某种方式实现生产者/消费者体系结构:

On listening thread:
aManualResetEvent.WaitOne ();

On the "producer" thread:
aManualResetEvent.Set ();
在.NET framework中有一些高级类可能会有所帮助,例如BlockingCollection


希望这有助于

假设您的类在自己的线程中运行,唯一的逻辑是执行来自其他线程的incomming调用,这将是解决方案: (内部评论)

公共类MyClass
{
私有对象对象1;
私有的另一个对象对象2;
公共MyClass()
{
object1=新的SomeObject();
object2=新的另一个对象();
object1.athreadeEvent+=ThreadedEventHandler1;
object2.AnotherThreadedEvent+=ThreadedEventHandler2;
}
//这在它自己的线程中运行!
//仅将实际函数调用添加到队列
public void ThreadedEventHandler1()
{
添加(ThreadedEventHandler1\u真的);
}
私有的void threadedeventhandler(真的)
{
//在这里做事
}
//这在它自己的线程中运行!
//仅将实际函数调用添加到队列
public void ThreadedEventHandler2()
{
添加(ThreadedEventHandler2_真的);
}
//下面是函数的实际逻辑
私有的虚空线程甚至是线程
{
//在这里做事
}
//任务的队列
BlockingCollection任务=新建BlockingCollection();
//此方法永远不会返回,它将永远被阻止
//i的唯一用途是在函数添加到队列时进行函数调用
//它是在这个实例的线程中完成的
公共无效StartConsume()
{
foreach(tasks.getconsumineGenumerable()中的操作)
{
//在调用之前添加逻辑
动作();
//在调用后添加逻辑
}
}
}
基于调用方线程tat调用函数ThreadedEventHandler1和ThreadedEventHandler2的解决方案实际上是将实际调用添加到队列中,并立即继续运行

另一方面,StartConsume函数迭代队列并对添加的方法调用进行调用。如果您想在调用前后添加另一个逻辑,可以将其添加到此函数中


希望它有助于实现您的目标。

但不完全理解设计背后的合理性。我可以说你要解决的问题以前已经解决过很多次了

我将假设您的主对象类似于一个服务,它期望来自自身和其他服务(子对象)的调用(在本例中为事件)。如果你从服务的角度考虑(你可以说应该这样做),WCF为你解决了这个问题,让你完成@Rami建议的所有繁重工作

使用以下行为定义主服务:
实例上下文模式-单个
并发模式-单个
更多关于这些

每个事件处理程序都会调用主服务来通知它事件


我很确定您不会走那么远并实施,但我认为它无论如何都值得作为一个选项提供。

好的,根据您的所有反馈(谢谢!),我有一个解决方案
On listening thread:
aManualResetEvent.WaitOne ();

On the "producer" thread:
aManualResetEvent.Set ();
public class MyClass
{
    private SomeObject object1;
    private AnotherObject object2;

    public MyClass()
    {
        object1 = new SomeObject();
        object2 = new AnotherObject();

        object1.AThreadedEvent += ThreadedEventHandler1;
        object2.AnotherThreadedEvent += ThreadedEventHandler2;
    }

    // This runs in its own thread!
    // Only add the real function call to the queue
    public void ThreadedEventHandler1()
    {
        tasks.Add(ThreadedEventHandler1_really);
    }

    private void ThreadedEventHandler1_really()
    {
        // DO STUFF HERE
    }

    // This runs in its own thread!
    // Only add the real function call to the queue
    public void ThreadedEventHandler2()
    {
        tasks.Add(ThreadedEventHandler2_really);
    }

    // here is the actual logic of your function
    private void ThreadedEventHandler2_really()
    {
        // DO STUFF HERE
    }

    // the queue of the tasks
    BlockingCollection<Action> tasks = new BlockingCollection<Action>();

    // this method never returns, it is blocked forever 
    // and the only purpose of i is to do the functions calls when they added to the queue
    // it is done in the thread of this instance
    public void StartConsume()
    {
        foreach (Action action in tasks.GetConsumingEnumerable())
        {
            // add logic before call
            action();
            // add logic after call
        }
    }
}