1

boost::interprocess::message_queue 机制似乎主要是为此而设计的:进程间通信。

问题是它序列化了消息中的对象:

“消息队列只是在进程之间复制原始字节,而不发送对象。”

这使得它完全不适合与正在传递的大型复合对象的快速和重复的线程间通信。

我想创建一个带有 ref/shared_ptr/pointer 的消息,指向一个已知的和以前创建的对象,并安全地将它从一个线程传递到下一个线程。

您可以使用 asio::io_service 并使用绑定完成发布,但这相当笨拙并且要求有问题的线程使用 asio,这似乎有点奇怪。

我已经编写了自己的,遗憾的是基于 asio::io_service,但更愿意切换到支持 boost 的通用机制。

4

2 回答 2

2

您需要一种为进程间通信而设计的机制,因为单独的进程具有单独的地址空间,并且除了非常特殊的情况外,您不能简单地传递指针。对于线程通信,您可以使用标准容器,如std::stackstd::queue并且std::priority_queue要在线程之间进行通信,您只需要通过互斥锁提供适当的同步。或者你可以使用 boost 提供的无锁容器。线程间通信还需要什么?

于 2013-10-24T21:12:43.703 回答
2

虽然我本身不是 Boost 方面的专家,但通过管道、消息队列等在进程和线程之间进行通信存在根本性的困难,特别是如果假设程序的数据是包含动态分配内存的类(这很用 Boost 编写的东西就是这样;字符串不像 C 语言那样是一个简单的对象……)。

复制类中的数据

消息队列和管道确实只是将字节集合从一个线程/进程传递到另一个线程/进程的一种方式。通常,当您使用它们时,您正在寻找目标线程以得到原始数据的副本,而不仅仅是对数据的引用的副本(这将指向原始数据)。

使用一个完全不包含指针的简单 C 结构,这很容易;结构的副本包含所有数据,没问题。但是,具有复杂数据类型(如字符串)的 C++ 类现在是一个包含指向已分配内存的引用/指针的结构。复制结构,您实际上并没有复制分配内存中的数据。

这就是序列化的用武之地。对于两个进程通常不能共享相同内存的进程间通信,序列化作为一种​​将要发送的结构以及它引用的所有数据打包成一个字节流的方式,这些字节流可以在另一端。对于线程,如果您不希望两个线程同时访问相同的内存,则没有什么不同。序列化是一种方便的方法,可以让您不必在类中导航以查看需要复制的确切内容。

效率

我不知道 Boost 使用什么来进行序列化,但显然序列化为 XML 会非常低效。像 ASN.1 BER 这样的二进制序列化会快得多。

此外,通过管道、消息队列复制数据不再像以前那样低效。传统上,程序员不会这样做,因为他们认为重复复制数据只是为了与另一个线程共享数据而浪费时间。使用涉及大量缓慢且浪费的内存访问的单核机器。

但是,如果考虑一下 QPI、Hypertransport 等时代的“内存访问”是什么,它与一开始只是复制数据并没有太大的不同。在这两种情况下,它都涉及通过串行总线将数据从一个内核的内存控制器发送到另一个内核的缓存。

今天的 CPU 是真正的 NUMA 机器,其内存访问协议位于串行网络之上,以伪造 SMP 环境。以通过管道、消息队列等复制消息的方式进行编程肯定会倾向于说一个人满足于 NUMA 的想法,并且实际上你根本不需要 SMP。

此外,如果您将所有线程间通信都作为消息队列进行,那么它们与管道并没有太大的不同,而管道与网络套接字也没有太大的不同(至少在 Not-Windows 上是这种情况)。因此,如果您仔细编写代码,您最终会得到一个可以跨分布式计算机网络或跨单个进程内的多个线程重新部署的程序。这是获得可扩展性的好方法,因为当您扩大规模时,您不会以任何重大方式改变程序的形状或感觉。

附加福利

根据所使用的序列化技术,可能会有一些附带好处。使用 ASN.1,您可以指定一个消息模式,在其中设置消息内容的有效范围。例如,您可以说一条消息包含一个整数,它的值可以介于 0 到 10 之间。由体面的 ASN.1 工具生成的编码器和解码器将自动检查您发送或接收的数据是否符合该约束,如果不是则返回错误。

如果 Google Protocol Buffers 等其他序列化程序没有为您进行类似的约束检查,我会感到惊讶。

好处是,如果您的程序中有错误并且您尝试发送不符合规范的消息,序列化程序将自动为您发现该错误。这可以节省大量的调试时间。此外,如果您共享内存缓冲区并使用信号量而不是使用消息队列来保护它,那么您肯定不会得到它。

CSP

通信顺序进程和 Actor 模型基于通过消息队列、管道等发送数据副本,就像您正在做的那样。CSP 尤其值得关注,因为它是避免多线程软件的许多陷阱的好方法,这些陷阱可能潜伏在源代码中而未被发现。

您可以使用一些 CSP 实现。有 JCSP,一个 Java 类库,和 C++CSP,建立在 Boost 之上,为 C++ 做 CSP。他们都来自肯特大学。

C++CSP 看起来很有趣。它有一个名为 csp::mobile 的模板类,有点像 Boost 智能指针。如果您通过通道(CSP 对消息队列的说法)将其中一个线程从一个线程发送到另一个线程,那么您发送的是引用,而不是数据。但是,模板会记录哪个线程“拥有”数据。所以接收手机的线程现在拥有数据(实际上并没有移动),发送它的线程不能再访问它。因此,您无需复制数据的开销即可获得 CSP 的好处。

看起来 C++CSP 也能够通过 TCP 进行通道;这是一个非常有吸引力的功能,放大是一个非常简单的可能性。JCSP 也可以通过网络连接工作。

于 2013-10-24T21:13:09.253 回答