我有一个类Communicator
,它在后台线程中工作,接收TCP端口上的数据。
Communicator的活动OnDataReceived
属于EventHandler<DataReceivedEventArgs>
类型。
还有另一个类Consumer
,其中包含订阅Communicator.OnDataReceived
事件的方法。
comm.OnDataReceived += consumer.PresentData;
Consumer
类在Form构造函数中创建,然后在另一个线程上调用其中一个方法。此方法是无限循环,因此它在应用程序执行期间保留在该方法中。
我想要做的是Communicator.OnDataReceived
事件在消费者的线程上调用consumer.PresentData
方法。
这几乎可能吗?如果是,我应该使用什么样的机制(同步类)?
答案 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
属性,可以通过调用Elapsed
或ISynchronizeInvoke.Invoke
将ISynchronizeInvoke.BeginInvoke
事件封送到托管同步对象的线程上。
棘手的部分是如何在ISynchronizeInvoke
类上实现Consumer
。该类启动的工作线程必须实现生产者 - 消费者模式才能处理委托。 BlockingCollection
课程会使这相对容易,但仍然存在相当大的学习曲线。如果您需要更多帮助,可以试一试并回复一个更有针对性的问题。