【问题标题】:ZeroMQ: how to reduce multithread-communication latency with inproc?ZeroMQ:如何使用 inproc 减少多线程通信延迟?
【发布时间】:2020-03-07 05:00:14
【问题描述】:

我正在使用inprocPAIR 来实现线程间通信,并试图解决由于轮询引起的延迟问题。如果我错了,请纠正我:轮询是不可避免的,因为普通的 recv() 调用通常会阻塞并且不能进行特定的超时。

在我目前的情况下,在 N 个线程中,每个 N-1 工作线程都有一个主 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 实际上是一种伪装的网络通信模式,因此一些延迟是不可避免的?

【问题讨论】:

    标签: c++ multithreading networking zeromq latency


    【解决方案1】:

    上面发表的声明(一个假设):

    “...普通的recv() 调用通常阻塞并且不能采取特定的超时。”

    不正确:
    一个普通的.recv( ZMQ_NOBLOCK )-call 永远不会“阻止”
    一个普通的 .recv( ZMQ_NOBLOCK )-call 可以被修饰以模仿 “特定超时”

    上面发表的声明(假设):

    "...使用超时轮询...引入延迟,延迟参数通常为1000ms.

    不正确:
    - 无需使用超时轮询
    - 少一个不需要设置 1000 毫秒的代码-“注入”-延迟,显然只花费在-no-new-message 状态

    Q“当使用常规的mutexstd::atomic 数据操作时,我能否达到典型的延迟?”

    是的。

    Q“我应该明白inproc实际上是一种伪装的网络通信模式,因此一些延迟是不可避免的吗?”

    没有。 inproc-transport-class 是所有这些类型中最快的,因为它主要是无协议/无堆栈的,并且与最终的快速指针机制有关,例如双端环形缓冲区指针管理。


    最好的下一步:

    1 )
    重构您的代码,以便始终利用零等待 { .poll() | .recv() }-方法,为这两个事件正确修饰- | no-event- }-specific 循环。

    2)
    如果那么愿意从智能循环检测周转时间中减少最后几个[us],可以专注于改进Context()-instance 将其设置为使用大量的nIOthreads &gt; N“幕后”。

    可选 3)
    对于几乎硬实时系统的设计,可以最终利用确定性驱动的Context()-threads' 和这些执行载体到特定的非重叠 CPU 内核的特定套接字映射(使用精心制作的亲和图)


    在代码中设置了 1000 [ms],没有人会抱怨将这 1000 [ms] 用于超时等待,由她/他自己编码。没有理由这样做。

    不要将行为归咎于 ZeroMQ,它是从 API 的应用程序端编码的。

    从不。

    【讨论】:

    • 谢谢。这在某种程度上是一种解脱。一开始,我尝试使用默认的-1 超时进行轮询,这可能会永远阻塞,然后似乎总是错过几毫秒,直到我使用1000ms。刚刚尝试使用0ms timeout 进行轮询,它可以工作。
    猜你喜欢
    • 2012-08-10
    • 2022-11-02
    • 1970-01-01
    • 1970-01-01
    • 2019-05-25
    • 1970-01-01
    • 1970-01-01
    • 2018-04-10
    • 2020-08-14
    相关资源
    最近更新 更多