【问题标题】:How to wake up threads with ZeroMQ如何使用 ZeroMQ 唤醒线程
【发布时间】:2017-11-21 06:24:06
【问题描述】:

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

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

【问题讨论】:

  • 如何先启动线程,然后使用zmq_recv 对消息队列进行阻塞读取,以便线程休眠直到收到消息?
  • 感谢您的建议。但是阻塞读取的问题是我的线程实际上会被阻塞。而且我不能承受我的线程大部分时间都被阻塞。 :)

标签: c++ multithreading zeromq


【解决方案1】:

ZMQ 是关于 Actor 模型编程的。它是一个反应堆。您使用 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 上运行。

在反应器系统的顶部合成前摄器相对简单。您的反应器循环只是成为一个事件调度程序。

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

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

【讨论】:

  • 很好的答案,欣赏操作系统内部更广泛的视角和细节水平。优秀的文字。祝你收到很多明信片。
  • @user3666197,不客气。还没有任何明信片...我正在考虑在 Windows 10 Linux 运行时上进行一些实验,看看是否可以发现一些线程弹出。
【解决方案2】:

正如您所提到的“我还希望我的线程在有新内容时唤醒。”,如果将回调分配给数据接收,则可以实现这一点 strong>,因此每当有新数据到达时,就会执行回调。

但是在阅读了 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 信号时,才会再次执行此处理程序。

【讨论】:

  • 当我研究 zmq 时,我担心是这种情况,而 zmq 不提供此功能。但我希望其他人能纠正我。所以谢谢你的确认。仍然希望有一个替代信号处理的方法。
  • 如果您对信号和处理程序有点担心,一个好的选择是通过文件描述符传递信号。查找signalfd。然后,您可以接收信号,使用 zmq_poll() 在发送信号时唤醒。这非常好,因为可以在对可以调用哪些函数的零限制的情况下处理信号(它都是同步的)。
猜你喜欢
  • 2011-08-05
  • 2018-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 2015-07-31
相关资源
最近更新 更多