ZeroMQ (C++):使用 inproc 发送指针

问题描述 投票:0回答:1

我正在尝试使用 ZeroMQ (zmq) 来实现多线程程序。 我想避免复制需要传递给工作人员的数据。 我的想法是将指向数据的指针作为 zmq 消息发送。 有用。但我发现,如果我更改工作人员内部的数据,生产者(主要)不会看到它。我想知道为什么!

这是一个例子:

#include <cstring>
#include <iostream>
#include <memory>
#include <thread>
#include <zmq.h>

struct Foo {
  explicit Foo(int value) noexcept
  : _value{value}
   {}
  int _value;
};

void worker(void *context)
{
  void *receiver = zmq_socket(context, ZMQ_REP);
  zmq_connect(receiver, "inproc://example");

  std::this_thread::sleep_for(std::chrono::milliseconds(20));

  zmq_msg_t message;
  zmq_msg_init(&message);
  zmq_msg_recv(&message, receiver, 0);

  char received[sizeof(Foo *)];
  std::memcpy(&received, zmq_msg_data(&message), sizeof(Foo *));

  //reinterpret_cast<Foo *>(received)->_value = 111;
  std::cout << "Received Foo with value: " << reinterpret_cast<Foo *>(received)->_value << std::endl;
  std::cout << "   the address of value: " << std::addressof(reinterpret_cast<Foo *>(received)->_value) << std::endl;

  zmq_msg_close(&message);
  zmq_close(receiver);
}

int main()
{
  void *context = zmq_ctx_new();
  void *sender = zmq_socket(context, ZMQ_REQ);
  zmq_bind(sender, "inproc://example");

  std::jthread thread{worker, context};

  auto foo{ std::make_unique<Foo>(42) };

  std::cout << "Sending Foo with value : " << foo->_value << std::endl;
  std::cout << "   the address of value: " << std::addressof(foo->_value) << std::endl;

  zmq_msg_t message;
  zmq_msg_init_size(&message, sizeof(Foo *));
  std::memcpy(zmq_msg_data(&message), foo.get(), sizeof(Foo *));
  zmq_msg_send(&message, sender, 0);

  zmq_msg_close(&message);
  zmq_close(sender);
  zmq_ctx_destroy(context);

  return 0;
}

我得到的输出是

Sending Foo with value : 42
   the address of value: 0x55a8c6fe2a90
Received Foo with value: 42
   the address of value: 0x7f30c0a52d40

正如你所看到的,

_value
的地址是不同的,这就解释了为什么改变worker内部的值不会影响生产者。但我不明白为什么它们不同。

最后,这是否是发送数据而不通过

inproc
传输复制数据的正确方法?

zeromq
1个回答
0
投票

Q2:
“(...)这是否是发送数据而不通过

inproc
传输复制数据的正确方法?”

好吧,
在 ZeroMQ Zen-of-Zero 中,其中包括保证接收原始版本的二进制精确副本,或者根本不接收任何内容(保证内容一致性,而不是交付),
是的

剩下的就在你的设计中了。

inproc
传输被称为最快(零复制、内存指向)、最小开销、ZeroMQ 工具箱内可用的传输。

Q1 :
“我想知道为什么!(?)”

您的“外部”代码没有任何线索,您不仅移动指针,而且还生成(在高度优化编译器的“视线”之外)底层(仅弱引用)值的更改。

出现冲突并不奇怪,因为编译器从源代码中没有任何线索,这将在它返回“后面”发生。

可以尝试检查和比较编译器处理(是的,阅读实际使用的汇编指令)公共变量声明与

volatile
标记的指定变量的不同方式,以获得一些想法,可以使用哪些防御策略来不让“发起者”依赖于(这里不够充分)优化编译器“假设”。

参考文档通常对这些“惊喜”非常清楚,如下所示:

21.2 易失性变量和字段

GNU C 编译器经常执行优化,消除写入或读取变量的需要。(...)

如果包含 foo 的内存与另一个程序共享,或者由硬件异步检查,则此类优化可以使沟通混乱。使用

volatile
是预防它们的一种方法。

在变量或字段声明中使用类型编写
volatile
表示该值可能随时因程序控制之外的原因而被检查或更改。因此,程序必须以谨慎的方式执行,以确保与这些访问进行正确的交互(无论何时发生)。

© www.soinside.com 2019 - 2024. All rights reserved.