我使用inproc
和PAIR
来实现线程间通信,并试图解决由于轮询而导致的延迟问题。如果我错了,请纠正我:轮询是不可避免的,因为普通的recv()
调用通常会阻塞,并且不能占用特定的超时时间。
在我目前的情况下,在N个线程中,每个N-1
工作线程都有一个main while循环。第N个线程是一个控制器线程,它将随时通知所有工作线程退出。但是,工作线程必须使用带有超时的轮询来获取quit
消息。这引入了延迟,延迟参数通常为1000ms
。
以下是的示例
while (true) {
const std::chrono::milliseconds nTimeoutMs(1000);
std::vector<zmq::poller_event<std::size_t>> events(n);
size_t nEvents = m_poller.wait_all(events, nTimeoutMs);
bool isToQuit = false;
for (auto& evt : events) {
zmq::message_t out_recved;
try {
evt.socket.recv(out_recved, zmq::recv_flags::dontwait);
}
catch (std::exception& e) {
trace("{}: Caught exception while polling: {}. Skipped.", GetLogTitle(), e.what());
continue;
}
if (!out_recved.empty()) {
if (IsToQuit(out_recved))
isToQuit = true;
break;
}
}
if (isToQuit)
break;
//
// main business
//
...
}
更糟糕的是,当主循环有嵌套循环时,工作线程需要在嵌套循环的每一层中包含更多的轮询代码。非常难看。
我之所以选择ZMQ进行多线程通信,是因为它的优雅和摆脱线程锁定的潜力。但我从未意识到投票的开销。
当使用常规互斥或std::atomic
数据操作时,我是否能够实现典型的延迟?我是否应该理解inproc
实际上是一种伪装的网络通信模式,因此不可避免地会出现一些延迟?
上述声明(假设(:
"…一个普通的
recv()
调用将通常阻塞并且不能进行特定的超时。">
不正确:
普通.recv( ZMQ_NOBLOCK )
-调用永远不会"阻塞">,
纯.recv( ZMQ_NOBLOCK )
-调用可以被修饰以模仿"特定超时">
上述声明(假设(:
"…有使用超时轮询…引入延迟,延迟参数通常为1000ms。">
不正确:
-不需要使用带超时的轮询
-较少的不需要设置1000ms代码-"注入"-延迟,显然只用于没有新消息状态的
Q:"使用常规
mutex
或std::atomic
数据操作时,我能达到典型的延迟吗?">
是。
Q:"我应该明白
inproc
实际上是一种伪装的网络通信模式,因此不可避免地会出现一些延迟吗?">
否inproc
-transport类是所有这些类型中速度最快的,因为它主要是无协议/无堆栈的,并且更多地与最终的快速指针机制有关,例如在双端环形缓冲区指针管理中。
最佳下一步:
1(
重新考虑您的代码,以便始终利用零等待{ .poll() | .recv() }
方法,为两个{event-|no-event-}特定的循环进行适当的修饰。
2(
如果那么愿意从智能环路检测周转时间中减少最后几个[us]
,可以专注于改进的Context()
-例如将其设置为"在引擎盖下"使用大量nIOthreads > N
。
可选3(
对于几乎是硬实时系统的设计,可以最终利用确定性驱动的Context()
线程和套接字特定的映射,将这些执行工具映射到特定的、不重叠的CPU核心上(使用精心制作的亲和映射(
在代码中设置了1000毫秒后,没有人会抱怨自己在超时时等待了1000毫秒。没有这样做的借口。
不要将行为归咎于ZeroMQ,它是从API的应用程序端编码的。
从来没有。