我当前的程序是通过zmq接收消息。线程不时醒来并检查zmq最后收到的内容。
现在我想改进这个设置并实际处理所有进来的消息,而不仅仅是唤醒时的最后一条消息。此外,当有新的东西时,我希望我的线程只是醒来。有没有一种方法可以让zmq强制我的线程唤醒,只要他为我的线程发送了一条新消息。
答案 0 :(得分:3)
ZMQ是关于Actor Model编程的。它是一个反应堆。您使用 zmq_poll()
等待一组套接字中的一个准备好进行阅读。当你这样做时,你会从中读取。
所以你的程序变成了一个循环,在顶部你调用zmq_poll()
阻塞(或超时),然后你处理准备好读取的那个套接字,然后循环。
相比之下,回调属于Proactor模型编程。你说前面应该发生什么(通过设置回调),然后无论如何都会发生。
要记住的重要一点是,Reactor和Proactor真的,真的不能混合。如果你可以帮助它们,千万不要试图将两者融合在一起,这将是无尽混乱的根源。或者全押其中一个。这就是ZeroMQ没有回调机制的原因。
两种风格混合不佳是为什么ZeroMQ的IPC传输在Windows上无法运行的原因。 ZeroMQ的反应堆是zmq_poll()
。从根本上说,这依赖于提供反应堆的操作系统。在Linux上,这是select()
或epoll()
,它是* nix的标准,可以与任何文件描述符(网络套接字,IPC管道,串行端口等)一起使用。
相比之下,Windows不提供通用反应器。它最接近的是select()
,它只适用于网络套接字。要在Windows中的管道上阻止您使用回拨的呼叫。从根本上说,Windows是一个先行者系统,关于它并没有太多的工作要做。这就是为什么像Boost.Asio
这样的东西也是先行者 - 它可以在Windows上运行。
在反应器系统顶部合成一个反应器是相对简单的。您的reactor循环只是一个事件调度程序。
相比之下,在Windows上进行高效反应堆的尝试必须求助于轮询线程。这就是cygwin所做的。他们对POSIX select()
的实现运行每个文件描述符的一个线程(这只是Windows HANDLE的抽象),每个人都忙于轮询他们的句柄,等待查看是否可以读取数据。效率不高。
现在真正有趣的是,微软现在已经为Windows 10完成了Linux运行时。这是一个系统调用级实现(与cygwin完全不同)。这确实支持epoll()
,select()
所需的系统调用,它确实适用于管道,套接字等。我真正想知道的是他们是如何实现这一目标的?明信片上的答案请...
答案 1 :(得分:2)
正如你所提到的“我也希望我的线程只是唤醒,当有新的东西时。”,如果为数据接收分配了一个回调,这可以实现,所以每当有新数据到达时,都会执行回调。
但在阅读了关于zeromq并参考以下链接之后:
Is it possible to add an event handling to ZeroMQ to act when data is received/sent?
Does ZeroMQ have a notification/callback event/message for when data arrives?
看起来截至目前,ZeroMQ没有实现任何在收到新数据时通知我们的回调。您可以通过在阻塞模式下使用zmq_recv来检查新数据(但这并不意味着在新数据出现时唤醒,它会一直阻塞直到数据到来)。
上述链接中所述的一种可能的解决方案:
您可以使用 SIGNALS以及zmq ,它会在新数据到达时通知。使用信号处理程序/中断处理程序的优点是,在新数据到达之前它们不会阻塞您的代码(如阻塞模式下的zmq_recv()
),而是信号处理程序将被唤醒并且只有在特定信号出现时才会执行接收。
SIGUSR1
/ SIGUSR2
等信号可用作新数据到达的指示。将信号处理程序分配给SIGUSR1
以在到达新数据时执行代码。
要在有新数据时唤醒进程,您必须发送数据并发送信号,如:
zmq_send(...);
kill(pid, SIGUSR1); /* Send signal SIGUSR1 to the process
whose PID is specified
*/
接收过程接收信号SIGUSR1
并唤醒以执行其处理程序。在此信号的处理程序中,您可以读取数据并执行某些任务。现在,只有当您发送一些新数据和SIGUSR1
信号时,才会再次执行此处理程序。