C# 在另一个线程中运行事件处理程序(无线程阻塞)

C# 在另一个线程中运行事件处理程序(无线程阻塞),c#,multithreading,event-handling,synchronize,C#,Multithreading,Event Handling,Synchronize,我有一个类Communicator,它在后台线程中工作,在TCP端口上接收数据 通讯器有一个事件OnDataReceived,属于EventHandler类型 还有另一个类Consumer,它包含订阅了Communicator.OnDataReceived事件的方法 comm.OnDataReceived += consumer.PresentData; Consumer类在表单构造函数中创建,然后在另一个线程上调用它的一个方法。此方法是一个无限循环,因此在应用程序执行期间它将保留在该方法中

我有一个类
Communicator
,它在后台线程中工作,在TCP端口上接收数据

通讯器有一个事件
OnDataReceived
,属于
EventHandler
类型

还有另一个类
Consumer
,它包含订阅了
Communicator.OnDataReceived
事件的方法

comm.OnDataReceived += consumer.PresentData;
Consumer
类在表单构造函数中创建,然后在另一个线程上调用它的一个方法。此方法是一个无限循环,因此在应用程序执行期间它将保留在该方法中

我要做的是让
Communicator.OnDataReceived
事件在使用者线程上调用
consumer.PresentData
方法


这几乎是可能的吗?如果是,我应该使用什么样的机制(同步类)

尝试使用该类。

这应该是可能的。您可以创建一个队列来执行,或者查看Dispatcher对象,将一些方法推送到UI线程中是很有用的(有时是强制的,这是唯一的方法),这很有帮助。

在代码中的某个地方添加这一点:(我通常将其放在名为ISynchronizedInvoke的静态助手类中,以便调用ISynchronizedInvoke.Invoke(…)

然后在OnDataReceived中,您可以执行以下操作:

Invoke(consumer, () => consumer.PresentData());
这将调用“consumer”上的“consumer.PresentData”

对于您的设计问题(用户参考通信器),您可以在通信器中引入一种方法,例如:

class Communicator {
    private ISynchronizeInvoke sync;
    private Action syncAction;

    public void SetSync(ISynchronizeInvoke sync, Action action) {
        this.sync = sync;
        this.syncAction = action;
    }

    protected virtual void OnDataReceived(...) {
        if (!sync.InvokeRequired) {
            syncAction();
        }
        else {
            object[] args = new object[] { };
            sync.Invoke(action, args);
        }
    }
}
这将为您提供一种从consumer类传入ISynchronizedInvoke的方法。因此,您将在使用者程序集中创建ISynchronizedInvoke

class Consumer {
    public void Foo() {
        communicator.SetSync(this, () => this.PresentData());
    }
}
因此,基本上您正在创建执行调用所需的所有内容,并将其传递给通信器。这就解决了在communicator中有一个实例或对使用者的引用的必要性


还要注意的是,我没有对这些进行任何测试,我只是在理论上进行测试,但它应该可以很好地工作。

只有当目标线程被设计为接受封送处理操作,将方法的执行从启动线程转移到目标线程时,才可以获得在线程上执行的方法

实现此功能的一种方法是让
使用者
类实现
ISynchronizeInvoke
。然后让您的
Communicator
类接受一个
ISynchronizeInvoke
实例,该实例可用于执行封送处理操作。以
System.Timers.Timer
类为例
System.Timers.Timer
具有
SynchronizationObject
属性,它可以通过调用
ISynchronizeInvoke.Invoke
ISynchronizeInvoke.BeginInvoke来将
已用事件封送到承载同步对象的线程上


棘手的部分是如何在
Consumer
类上实现
ISynchronizeInvoke
。该类启动的工作线程必须实现生产者-消费者模式才能处理委托。
BlockingCollection
类将使这相对容易,但仍有相当长的学习曲线。如果您需要更多帮助,请试一试,然后用一个更集中的问题发回。

抱歉,但当您说消费者在其他线程上运行时,还不是很清楚。是否有运行无限线程的函数,或者对象是在其他线程中创建的,或者是其他什么?Tigran我已经更新了问题以回答您的评论。这里的“消费者线程”是哪个线程?GUI线程还是另一个?@Henk有三个线程。一个是GUI(启动)线程,另一个是TCP线程,第三个是使用者的线程。好的,您希望事件发生在哪个线程上?问题是,通信器位于单独的程序集中,没有对使用者类的引用。反过来说。如果我理解您的答案,我客户的类将必须实现ISynchronizeInvoke。以前从未用过。更不用说试图实施它了。“我得调查一下。”丹德森:是的,我知道。我无法合并程序集,因此唯一要做的就是实现ISynchronizeInvoke并将使用者的实例作为ISynchronizeInvoke对象传递。但我想知道我是否有一些设计缺陷。
class Consumer {
    public void Foo() {
        communicator.SetSync(this, () => this.PresentData());
    }
}