ZeroMQ唤醒线程的方法

How to wake up threads with ZeroMQ

我当前的程序正在通过 zmq 接收消息。一个线程不时醒来并检查 zmq 最后收到了什么。

现在我想改进此设置并实际处理收到的每条消息,而不仅仅是醒来时的最后一条消息。此外,我希望我的线程在有新内容时醒来。 zmq 有没有一种方法可以强制唤醒我的线程,只要他对我的线程有新消息。

正如您提到的“我还希望我的线程在有新内容时唤醒。”,这可以通过回调实现分配给数据接收,所以每当有新数据到达时,回调就会被执行。

但是在阅读了 zeromq 并参考了以下链接之后:

Does ZeroMQ have a notification/callback event/message for when data arrives?

目前看来,ZeroMQ 没有实现任何在接收到新数据时通知我们的回调。您绝对可以通过在阻塞模式下使用 zmq_recv 来检查新数据(但这并不意味着在新数据到来时醒来,它只会一直阻塞直到数据到来)。

上述链接中所述的一种可能的解决方案:

您可以将 SIGNALS 与 zmq 一起使用,它会在新数据到达时发出通知。使用信号 handler/interrupt 处理程序的优点是它们不会在新数据到达之前阻塞您的代码(例如 zmq_recv() 在阻塞模式下),相反,信号处理程序会唤醒并且仅在特定的时候执行收到信号。

SIGUSR1/SIGUSR2这样的信号可以用作新数据到达的指示。将信号处理程序分配给 SIGUSR1 以在新数据到达时执行代码。

要在有新数据时唤醒进程,您必须发送数据并发送信号,例如:

zmq_send(...);

kill(pid, SIGUSR1);         /*  Send signal SIGUSR1 to the process
                                            whose PID is specified
                                                                   */

接收进程收到信号SIGUSR1并唤醒执行它的处理程序。在此信号的处理程序中,您可以读取数据并执行一些任务。现在,如果您发送一些新数据以及 SIGUSR1 信号,此处理程序只会再次执行。

ZMQ 完全是关于 Actor 模型编程的。它是一个反应堆。您使用 zmq_poll() 等待一组套接字中的一个准备好读取。当一个人这样做时,您会从中阅读。

所以你的程序变成了一个循环,在顶部你调用 zmq_poll() 阻塞(或超时),然后你处理任何已准备好读取的套接字,然后循环。

相比之下,回调属于 Proactor 模型编程。你说什么应该发生在前面(通过设置回调),然后无论如何都会发生。

要记住的重要一点是,Reactor 和 Proactor 真的,真的不要混用。如果可以的话,永远不要试图将两者混合在一起,这将是无尽混乱的根源。要么全力以赴。这就是 ZeroMQ 没有回调机制的原因。

这两种风格的糟糕混合是 ZeroMQ 的 IPC 传输在 Windows 上不起作用的原因。 ZeroMQ 的反应器是zmq_poll()。从根本上说,这依赖于 OS 提供的反应器。在 Linux 上,这是 select()epoll(),这是 *nix 的标准,可以使用任何文件描述符(网络套接字、IPC 管道、串行端口等)。

相比之下Windows不提供通用反应器。最接近的是 select(),它只适用于网络套接字。要阻止 Windows 中的管道,您可以使用接受回调的调用。 Windows 从根本上说是一个前摄系统,对此没有太多可做的。这就是为什么像 Boost.Asio 这样的东西也是 proactor - 它会在 Windows 上工作。

在反应器系统之上合成前驱器相对简单。您的反应器循环简单地变成了一个事件调度程序。

相比之下,在 Windows 上进行高效反应器的尝试必须求助于轮询线程。这就是 cygwin 所做的。他们的 POSIX select() 实现为每个文件描述符运行一个线程(这只是 Windows HANDLE 的抽象),每个文件描述符都忙于轮询他们的句柄,等待查看是否可以读取数据。效率不高。

现在真正非常有趣的是,Microsoft 现在已经为 Windows 10 完成了 Linux 运行时。这是一个系统调用级别的实现(与 cygwin 完全不同)。这确实支持 epoll()select() 所需的系统调用,并且它确实适用于管道、套接字等。我真的非常想知道他们是如何实现这一点的?请在明信片上回答...