繁体   English   中英

强制事件处理程序在对象的线程C#.NET上运行

[英]Force event handler to run on object's thread, C# .NET

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

如果这是一个Forms UI对象,则可以利用其对ISynchronizeInvoke接口的实现,并调用InvokeRequiredInvoke等。在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接口,但是这样做似乎ISynchronizeInvoke复杂。 在跳下兔子洞之前,我曾想过要对专家进行检查,看看是否有更简单的解决方案。

有什么想法吗?

编辑:

我想要在父对象的线程中运行事件处理程序的部分原因是因为父对象具有*自己的事件,这些事件是根据其成员对象发送的事件触发的。 我希望此类可以隐藏任何线程功能,因此使用该类的代码不必担心与线程相关的问题(例如,锁等)。 简单地锁定共享数据将无法完成任务,因为我仍然需要从线程事件处理程序中触发事件。

调用另一个线程的想法与一个while循环密切相关,该循环不时检查是否有“外部”消息要处理。 对于UI,有执行此操作的Windows循环。 对于外部线程,必须手动编写循环。 想象一下没有循环的情况,并且您有一个运行时间相对较长的线程,对吗? 并且突然想中断该线程以调用您的消息并在相同的共享堆栈内存上继续执行该操作。 这种中断会破坏您的堆栈。 这根本不可能。 另一种可能性是使用诸如ManualResetEvent之类的同步机制,只等待一个信号(线程外部的信号)。 因此,要恢复,为了处理来自另一个线程的消息,您基本上只有两个选择:

1)您有一个while循环,最终花了一点时间(给其他线程一些时间/滴答声来完成工作)

while (true) {
  Thread.Sleep (5);
  if (someMessageArrived) { ... }
}

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

On listening thread:
aManualResetEvent.WaitOne ();

On the "producer" thread:
aManualResetEvent.Set ();

.NET框架中有一些高级类可能会有所帮助,例如BlockingCollection。

希望这可以帮助

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

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

基于调用者线程tat调用以下函数的解决方案:ThreadedEventHandler1和ThreadedEventHandler2,实际上将实际调用添加到队列中并立即继续其运行。

另一方面,StartConsume函数迭代队列并进行添加的方法调用的调用。 如果要在调用之前和之后添加其他逻辑,则可以在此函数中添加它。

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

没有完全理解设计背后的原理。 我可以说您要解决的问题以前已经解决了很多次。

我将假设您的主要对象就像一个服务,期望它自身和其他服务(子对象)进行调用(在这种情况下为事件)。 如果您从服务方面考虑(您可能应该考虑),WCF会为您解决所有问题,从而解决@Rami建议的所有问题。

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

每个事件处理程序都会调用该主要服务,以通知该事件。

我敢肯定,您不会走那么远的距离并将每个类都实现为服务 ,但是无论如何它还是值得提供的。

好的,根据您的所有反馈(谢谢!),我已经解决了我的问题。 简短的答案:我想做的事是不可能的。

这是那些询问的人的更多详细信息。 我正在写一个DLL,用于管理连接到串行端口的设备。 这包括基本的串行端口COM(分组TX和RX,包括解析),以及更高级别的协议行为(TX,Ack,超时重试等)。.NET提供的串行端口事件处理程序显然是异步的,我用于处理超时等的System.Timers.Timer对象。

我正在围绕MVVM架构构建代码,因此我的UI完全没有任何逻辑。 因此,我需要避免利用UI提供的DispatcherInvoke功能。

我一直在寻找一种以WinForms和WPF提供的简单方式来处理DLL中异步事件的方法。 但是正如已经指出的那样,正如我在深入研究时所了解到的那样,当您调用BeginInvokeDispatcher将某些东西推入队列时,您实际上是在做什么,以后将由另一个查询该队列的线程来使用。 在UI上下文之外,不存在这种轮询架构。

所以。 我的选择是lock的共享对象以使其成为线程安全,或在模仿UI代码已经执行的操作的另一个线程中实现我自己的轮询体系结构(以避免阻塞使用DLL的程序)。

无论哪种情况,在处理DLL类中的事件时,UI代码仍将需要使用其Invoke或等效工具。 我想没关系。

暂无
暂无

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

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