in the framework of the ZEROMQ source analysis Note, you can see that there are two types of communication between threads, one is to send and receive commands, to tell the object what method to do and what to do, and the structure of the command determination of command_t structural body The other is the message communication between the socket_base_t instance and the session, and the structure of the message is msg_t OK. Commands are sent and stored through the mailbox_t implementation, the message is sent and stored through the pipe_t implementation, both structures will be detailed, today first said a thread between the sending and receiving commands.
ZEROMQ threads can be divided into two categories, one is IO thread, like reaper_t, io_thread_t belong to this category, such threads are characterized by the inclusion of a poll poller and mailbox_t, through the Poller can listen to mailbox_t letter arrives The other is the ZMQ socket, all socket_base_t instantiated objects can be considered as a separate thread, such threads do not contain poller, but also contain a mailbox_t, can be used to send and receive commands, because does not contain poller, Only in each use of the socket_base_t instance of the first time to deal with mailbox_t, to see if there are commands to deal with, the code is to call the following function each time to receive and process the command:
int zmq::socket_base_t::p rocess_commands (intbool throttle_)
In addition, two types of threads send commands in a consistent manner. Here's a detailed command structure, how to send a command, and how two types of threads receive commands
1. Command
First look at the command structure (detailed structure see source COMMAND.HPP):
This structure defines the commands so can be sent between threads. struct command_t { // Object to process the command. zmq::object_t *destination; Enum type_t { ... } type; Union { ... } args; };
The command is an object that tells another object to do something, which is plainly to tell which method an object should call, , and , and after the thread receives the command, it is sent to the appropriate object for processing according to the destination. You can see that the destination property of the command is of type object_t, when the hierarchy diagram of the class is described in the previous section, when it comes to object_t and its subclasses have the ability to send and process the command ( no command-function ), So it is necessary to clarify one thing, what is the relationship between object, object_t, Poller, Thread, mailbox_t, command?
- In ZMQ, each thread will have a mailbox, command and send the function is the bottom of the mailbox is implemented by the
- ZMQ provides the object_t class for the ability to send commands using a thread mailbox (the object_t class has other functions), object_t and the ability to handle commands.
- There is also a poller in the thread for the incoming command, and the thread receives the command and puts the command to object_t.
Simply put, object_t command , poller Monitoring command arrives to inform thread received command, to object_t processing, whether object_t or poller actually operate mailbox_t, and these three are bound on the same thread. Let's take a look at the specifics of how to send commands
2, send orders
An object that wants to use the thread's command function will inherit its class from object_t (source code in Object.hpp/.cpp):
class object_t { public : object_t (zmq::ctx_t *ctx_, UIn t32_t tid_); void Process_command (zmq::command_t & cmd_); ... protected : ... private *ctx; // uint32_t tid;//thread ID of the thread the the object belongs to. void Send_command (command_t &cmd_); }
as you can see, object_t contains a tid, meaning the mailbox_t of which thread the Object_t object is to use. About zmq::ctx_t, called context context in ZMQ, context is simply the survival environment of ZMQ, where storage is a global object, and all of the threads in the ZMQ can use these objects. The Mailbox_t object in the ZMQ thread is stored zmq in the Ctx_t object. ZMQ, in the context of the use of a container slots load thread mailbox, when the new thread, the thread is assigned a thread flag TID and mailbox, put mailbox into the container of the TID location, the code is slots[tid]= Mailbox With this foundation, thread A sends a command to thread B just to Slots[b.tid] to write the command:
voidZmq::object_t::send_command (command_t &cmd_) {CTX->send_command (cmd_.destination->Get_tid (), cmd_);}voidZmq::ctx_t::send_command (uint32_t tid_,ConstCommand_t &command_) {slots [tid_]-Send (Command_);}voidZmq::mailbox_t::send (ConstCommand_t &cmd_) {Sync.Lock(); Cpipe.write (Cmd_,false); BOOLOK =Cpipe.flush (); Sync.unlock (); if(!OK) signaler.send ();}
3. IO thread receiving command
as mentioned earlier, each IO thread contains a POLLER,IO thread structure as follows (source code in Io_thread_t.hpp/.cpp):
classio_thread_t: Publicobject_t, Publici_poll_events { Public: io_thread_t (zmq::ctx_t*ctx_, uint32_t tid_); ~io_thread_t (); voidStart ();//Launch the physical thread. voidStop ();//Ask underlying thread to stop. ... Private: mailbox_t mailbox;//I/O thread accesses incoming commands via this mailbox.poller_t::handle_t Mailbox_handle;//Handle associated with mailbox ' file descriptor.poller_t *poller;//The I/O multiplexing is performed using a Poller object.}zmq::io_thread_t::io_thread_t (ctx_t*ctx_, uint32_t tid_): object_t (Ctx_, tid_) {Poller=New(Std::nothrow) poller_t; Alloc_assert (Poller); Mailbox_handle= POLLER->ADD_FD (MAILBOX.GET_FD (), This); Poller-Set_pollin (Mailbox_handle);}
The constructor adds the mailbox_t handle to the Poller, allowing Poller to listen to its read event, so if a command is sent, Poller will be awakened and io_thread_t in_event be called:
voidzmq::io_thread_t::in_event () {//todo:do We want to limit number of commands I/O thread can//process in a single go?command_t cmd; intrc = Mailbox.recv (&cmd,0); while(rc = =0|| errno = = eintr) {//If there is content in the read pipeline or is interrupted while waiting for the signal, it will be read if(rc = =0) Cmd.destination-Process_command (CMD); RC= Mailbox.recv (&cmd,0); } errno_assert (RC!=0&& errno = =eagain);}
As you can see, In_event uses the function of the mailbox_t receive command. After receiving the command, call destination to process the command.
4. socket_base_t Thread receiving command
The previous section said that each instance of socket_base_t can be viewed as a zmq thread, but rather special, not using Poller, but to check for an unhandled command when using the following methods of the socket:
intZmq::socket_base_t::getsockopt (intOption_,void*optval_,size_t *Optvallen_)intZmq::socket_base_t::bind (Const Char*addr_)intZmq::socket_base_t::connect (Const Char*addr_)intZmq::socket_base_t::term_endpoint (Const Char*addr_)intZmq::socket_base_t::send (msg_t *msg_,intflags_)intZMQ::SOCKET_BASE_T::RECV (msg_t *msg_,intflags_)voidZmq::socket_base_t::in_event ()//This function is only used when destroying Socke, and when you talk about Zmq_close in the back.
The means of checking is to call the Process_commands method:
intzmq::socket_base_t::p rocess_commands (intTimeout_,BOOLThrottle_) { intRC; command_t cmd; if(Timeout_! =0) { //If We are asked to wait, simply ask mailbox to wait.rc = MAILBOX.RECV (&cmd, timeout_); } Else{Some code RC= Mailbox.recv (&cmd,0); } //Process all available commands. while(rc = =0) {cmd.destination-Process_command (CMD); RC= Mailbox.recv (&cmd,0); } Some code}
Visible, and ultimately, the ability to receive commands using mailbox_t.
Here's a question to consider, why does the thread that corresponds to the socket_base_t instance not use Poller? does it bother you to check every time you use the above methods?
For the implementation of mailbox_t, the following section describes
ZEROMQ Source Analysis Note thread-to-Send command (2)