在另一个线程中运行事件处理程序(没有线程阻塞)

时间:2011-07-07 19:22:25

标签: c# multithreading event-handling synchronize

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

Communicator的活动OnDataReceived属于EventHandler<DataReceivedEventArgs>类型。

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

comm.OnDataReceived += consumer.PresentData;

Consumer类在Form构造函数中创建,然后在另一个线程上调用其中一个方法。此方法是无限循环,因此它在应用程序执行期间保留在该方法中。

我想要做的是Communicator.OnDataReceived事件在消费者的线程上调用consumer.PresentData方法。

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

4 个答案:

答案 0 :(得分:4)

在代码中的某处添加:(我通常把它放在一个名为ISynchronizedInvoke的静态助手类中,这样我就可以调用ISynchronizedInvoke.Invoke(...));

public static void Invoke(ISynchronizeInvoke sync, Action action) {
    if (!sync.InvokeRequired) {
        action();
    }
    else {
        object[] args = new object[] { };
        sync.Invoke(action, args);
    }
}

然后在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);
        }
    }
}

这将为您提供一种从您的使用者类传递ISynchronizedInvoke的方法。因此,您将在使用者程序集中创建ISynchronizedInvoke。

class Consumer {
    public void Foo() {
        communicator.SetSync(this, () => this.PresentData());
    }
}

所以基本上你创建了调用所需的一切,并将它传递给你的沟通者。这解决了在通信器中拥有实例或引用消费者的必要性。

另请注意,我没有测试任何这个,我在理论上这样做,但它应该很好地工作。

答案 1 :(得分:1)

尝试使用BackgroundWorker类。

答案 2 :(得分:1)

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

答案 3 :(得分:1)

只有当目标线程被设计为接受将方法的执行从初始线程传输到目标线程的编组操作时,才能获得在线程上执行的方法。

让这一点发挥作用的一种方法是让Consumer类实现ISynchronizeInvoke。然后让您的Communicator类接受可用于执行编组操作的ISynchronizeInvoke实例。以System.Timers.Timer类为例。 System.Timers.Timer具有SynchronizingObject属性,可以通过调用ElapsedISynchronizeInvoke.InvokeISynchronizeInvoke.BeginInvoke事件封送到托管同步对象的线程上。

棘手的部分是如何在ISynchronizeInvoke类上实现Consumer。该类启动的工作线程必须实现生产者 - 消费者模式才能处理委托。 BlockingCollection课程会使这相对容易,但仍然存在相当大的学习曲线。如果您需要更多帮助,可以试一试并回复一个更有针对性的问题。