简体   繁体   English

是否可以通过多线程同时在ZMQ中使用Send / Recv?

[英]Is is possible to somehow use Send/Recv in ZMQ simultaniousely(by multithreading)?

I am trying to come up how to effectively use ZMQ to multithread (so send doesn't block receive and receive doesn't block send). 我正在尝试提出如何有效地使用ZMQ来实现多线程(因此send不阻止接收,receive不阻止发送)。

I wanted to use ZMQ_DONTWAIT flag but when sending the data, it will sometimes not be send ( EAGAIN error, so I would have to re-queue the message which is a waste of resources when dealing with megabytes of data). 我想使用ZMQ_DONTWAIT标志,但是在发送数据时,有时不会发送( EAGAIN错误,因此我必须重新排队该消息,这在处理兆字节数据时浪费资源)。

I did come up with the following code: 我确实提出了以下代码:

Concurrency::concurrent_queue<zmq::message_t> QUEUE_IN;
Concurrency::concurrent_queue<zmq::message_t> QUEUE_OUT;

void SendThread(zmq::context_t &context) {
    zmq::socket_t zmq_socket(context, ZMQ_DEALER);
    zmq_socket.connect(string_format("tcp://%s:%s", address, port).c_str());
    zmq::message_t reply;
    while (true) {
        while (QUEUE_OUT.try_pop(reply))
            zmq_socket.send(reply);
        Sleep(1);
    }
}

void RecvThread(zmq::context_t &context) {
    zmq::socket_t zmq_socket(context, ZMQ_DEALER);
    zmq_socket.connect(string_format("tcp://%s:%s", address, port).c_str());
    zmq::message_t reply;
    while (true) {
        while (zmq_socket.recv(&reply))
            QUEUE_IN.push(reply);
    }
}

void ConnectionThread()
{
    zmq::context_t context(1);
    std::thread* threads[2] = { 
        new std::thread(SendThread, context), 
        new std::thread(RecvThread, context)
    };
    threads[0]->join();
}

However that would require two sockets on the server end, and I would need to identify to which I need to send data and to which I need to listen on the server end, right? 但是,这将需要在服务器端使用两个套接字,而且我需要确定在服务器端需要向哪些对象发送数据以及向哪些对象进行侦听,对吗? Is there no way to use one socket yet use send and receive in a multithreaded environment? 有没有办法使用一个套接字,而在多线程环境中使用发送和接收呢?

I would maybe like to do it asychroniously on one socket, but after studying the async sample I still don't grasp the idea as there aren't much comments around it. 我可能想在一个套接字上异步地执行此操作,但是在研究了异步示例之后,由于周围没有太多评论,我仍然不了解这个想法。

  1. When sending large data (you say you're sending MB of data in a single message), it's going to take some time, ZMQ doesn't "duplex" sending and receiving so that they can both actually happen. 当发送大数据时(您说要在一条消息中发送MB的数据),这将需要一些时间,ZMQ不会“双工”发送和接收,因此它们实际上都可以发生。 The DONTWAIT flag isn't going to help you so much there, its purpose is to ensure that you're not waiting on ZMQ when you could be performing non-ZMQ actions. DONTWAIT标志不会对您有太大帮助,它的目的是确保您可以在执行非ZMQ动作时不等待ZMQ。 All messages should still be queued up in any event (barring interference from the High Water Mark) 无论如何,所有消息仍应排队(除非受到高水位标记的干扰)
  2. The only way to safely use multiple threads to parallelize sending and receiving is to use multiple sockets. 安全使用多个线程并行化发送和接收的唯一方法是使用多个套接字。

But, it's not all bad. 但是,这还不是全部。 If you use one designated send socket and one designated receive socket, then you can use pub/sub, which opens up some interesting options. 如果使用一个指定的发送套接字和一个指定的接收套接字,则可以使用pub / sub,这会打开一些有趣的选项。

Avoiding the Sleep 避免睡眠

To avoid the sleep, you can use zmq_poll() using a ZMQ_POLLOUT event to protect the send(). 为了避免睡眠,可以使用带有ZMQ_POLLOUT事件的zmq_poll()来保护send()。 You don't need to use ZMQ_DONTWAIT. 您不需要使用ZMQ_DONTWAIT。 [I used the C function there, your binding will have the equivalent.] [我在那里使用了C函数,您的绑定将具有等效功能。]

Routing to RecvThread 路由到RecvThread

One cannot share sockets between threads, so 2 sockets are needed for this to work. 一个不能在线程之间共享套接字,因此需要2个套接字才能工作。 The server would only need one socket (presumably ROUTER) that bound to 2 ports. 服务器仅需要一个绑定到2个端口的套接字(大概是ROUTER)。 When it receives a message, it will then need to know where to send the reply... 收到消息后,它将需要知道将回复发送到哪里...

When a ROUTER socket receives a message, the zmq internals adds a frame to the message with the identity of the sender. 当ROUTER套接字接收到消息时,zmq内部构件会在消息中添加带有发送者身份的帧。 This frame will be seen by the server code, which would normally use that same identity frame when constructing a message to reply to the sender. 服务器代码将看到此帧,服务器代码通常在构造消息以回复发件人时使用相同的标识帧。 In your case, that's the client's SendThread. 在您的情况下,这就是客户端的SendThread。 OTOH, you want to reply to the client's receive socket, so the identity frame must be for that. OTOH,您想回复客户端的接收套接字,因此必须使用身份框架。

The only thing left is how the server obtains the identity frame of the client's receive socket. 剩下的唯一事情就是服务器如何获取客户端接收套接字的标识帧。 For that, you'll need to invent a small protocol. 为此,您需要发明一个小型协议。 Arranging for the client's RecvThread to send one message to the server would almost be enough. 安排客户端的RecvThread向服务器发送一条消息就足够了。 The server should understand that message and simply retain the identity frame of the client's receive socket, and use a copy of it when constructing reply messages. 服务器应该理解该消息,并仅保留客户端接收套接字的标识框架,并在构造回复消息时使用它的副本。

All of this is explained in the guide under "Exploring ROUTER Sockets". 所有这些都在指南“探索ROUTER套接字”下进行了说明。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM