[英]How to implement a message pump in Non-UI thread in .NET?
我正在阅读此博客: 正确使用线程 ,我想知道:
如何在非UI线程中实现消息( 注意:此处不是Windows消息 )?
我想要的是消息可以是对象或命令,例如Action<T>
/ Func<T>
等。对于不同类型的消息,我是否必须使用单独的队列? 对对象说一个队列,对Action<T>
/ Func<T>
说一个队列?
考虑到消息的类型各不相同,如何实现?
编辑:
我要完成的是生产者/消费者模型,生产者/消费者共享队列进行通信,该队列不仅可以包含供消费者使用的对象,而且还可以将一些“ 命令 ”传递给消费者以执行。
术语“消息泵” 专门指Windows中的GUI线程(即使它们实际上并未创建GUI)。
听起来好像您在谈论消息队列 ,但是即使消息队列也不会接受动作或功能 ,而是会接受特定类型的消息 。
我当然从未听说过将Func<T>
传递到消息队列-结果将如何处理? 你要把它存放在某个地方吗? 发信息给来电者? 这对我来说似乎不是很有用。
无论您尝试执行哪种操作,听起来都可以通过内置的.NET线程池更好地处理。 那实际上是设计用于异步执行代码 ,而不是排队和处理消息 。
好吧,在启动线程之前,需要Thread.SetApartmentState()将线程切换到STA,然后在线程函数中调用Application.Run()以启动消息循环。
但是,这与Action和Func委托无关。 消息队列是一个内部Windows结构,用于存储鼠标和键盘消息。 不适合存储自己的对象。 不确定要在哪里使用。
常规的System.Collections.Generic.Queue <>对象可以存储任何类型的对象,包括代表“命令”的类的对象。 使用ReaderWriterLockSlim使其具有线程安全性。
我对您的最终目标感到有些困惑,但似乎您正在寻找两件事。
首先,如果要传递对象(大概是出于跨线程通信的目的而考虑使用消息队列?),这听起来像是要使用pub / sub类型的实现? 这是有据可查的,并且在C#中有很多示例
对于第二种情况,您想将委托作为消息的有效负载传递,我想您正在尝试在发布者和订阅者之间实现某种双向通信? 像回调一样?
这就是我感到困惑的地方。 这里的问题到底是什么? 您可以实现一个了解如何处理不同消息有效负载类型的消息队列。 像BroadcastMessage这样的东西,其中T是第一种情况的对象,第二种情况是委托(Func / action)。
我有一个Codeplex项目,它是消息队列的简单实现,我在MVC / MVVM应用程序中将其用于特定目的。 不确定这是您要找的内容,但可能会帮助您进一步阐明问题?
将此作为代码格式的单独答案
好的,所以在阅读您的更新后,我想您只是想要我在“第二种情况”中描述的内容
Broadcast<T>("Foo")
其中T是代表。
那你的消费者会做
Subscribe<T>("Foo",HandlerMethod)
因此,生产者消费者场景将如下所示
internal static class MessagePump
{
public static void Subscribe<T>(String foo, Action<String> handlerMethod)
{
throw new NotImplementedException();
}
public static void BroadcastMessage<T>(String foo, Action<String> someAction)
{
throw new NotImplementedException();
}
}
public class Producer
{
void SendMessage()
{
MessagePump.BroadcastMessage<Action<String>>("Foo", SomeAction);
}
void SomeAction(String param)
{
//Do Something
}
}
public class Consumer
{
public Consumer()
{
MessagePump.Subscribe<Action<String>>("Foo", HandlerMethod);
}
void HandlerMethod(String param)
{
// Do Something
}
}
这只是我脑海中不可思议的事,是一个人为的例子,所以请带一点盐。 这几乎就是我在先前发布的快递框架中所做的事情。 您可能需要深入研究该代码以获得更具体的实现示例。
您需要考虑如何管理使用者,如何验证广播和订阅以及针对特定情况,如何确保正确调用要传递的委托? 还是在乎?
这有帮助吗?
我想分享一个曾经为自己的研究写的基本消息队列。 这可能有助于了解可能的消息队列实现。 没有关于完整性,完整性或没有错误的主张,并且在大多数情况下,可能有比使用自定义消息队列更好的解决方案。
public void run()
{
while (running)
{
mainLoopWaitHandle.WaitOne();
EventHandlerFunction f = null;
while (running)
{
f = popEvent();
if (f == null) break;
f();
}
}
}
private void pushEvent(EventHandlerFunction handlerFunction)
{
lock (eventQueueLock)
{
int b = (queueInIndex + 1) & 255;
if (b == queueOutIndex)
{
throw new Exception("Buffer overflow in event queue.");
}
eventQueue[queueInIndex] = handlerFunction;
queueInIndex = b;
mainLoopWaitHandle.Set();
}
}
private EventHandlerFunction popEvent()
{
EventHandlerFunction ret = null;
lock(eventQueueLock)
{
int b = (queueOutIndex + 1) & 255;
if (queueOutIndex == queueInIndex)
{
mainLoopWaitHandle.Reset();
return null;
}
ret = eventQueue[queueOutIndex];
eventQueue[queueOutIndex] = null;
queueOutIndex = b;
}
return ret;
}
主线程通过运行run()
开始使用消息队列。 run()
是一个阻塞方法,直到将class属性running
设置为false
时才返回。 这可以通过下面介绍的调用程序方法完成。
为了在主线程上调用方法,实际上需要两种方法。 一个EventHandlerFunction(实际上是在主线程上被调用的方法A()
)和在调用者的线程上执行的方法B()
。 我看到这类似于UI函数,其中B()
是形式的方法,而A()
从B()
获取Invoke
d。
B()
调用A()
pushEvent(A);
而pushEvent()
和popEvent()
是线程保存的。
方法B()
的目的是在某个数据结构上存储任何对象或参数以进行数据传输,这些对象或参数表示方法A()
的参数(或待办事项A()
。 例如,此结构可以是带有工作项的List<>
。 为了线程安全,方法A()和B()都需要注意正确锁定此结构。
方法A()
还应考虑到缓冲区可以在消息队列或它自己的数据传输结构上完全运行,并且需要注意后果(例如,放弃调用或阻塞调用,直到堆栈上有空间为止) )。
希望能帮助到你。 欢迎捐款。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.