Mini-muduo Portal
Version 0.00 build muduo-1 from epoll mini-muduo Introduction
Version 0.01 build the simplest epoll muduo-2 from epoll
Version 0.02 adds the first class from epoll build muduo-3, By the way Reactor
Version 0.03 build muduo-4 from epoll join Channel
Version 0.04 adds acceptor and tcpconnection from epoll build muduo-5
Version 0.05 adds eventloop and epoll from epoll build muduo-6
Version 0.06 add imuduouser from epoll build muduo-7
Version 0.07 from epoll build muduo-8 add sending buffer and receiving buffer
Version 0.08 adds onwritecomplate echo and buffer from epoll build muduo-9
Version 0.09 build muduo-10 timer from epoll
Version 0.11 build a single-thread reactor network model for muduo-11 from epoll
Version 0.12 build muduo-12 multithreading code from epoll admission
Version 0.13 build muduo-13 reactor + threadpool molding from epoll
For Mini-muduo v0.13 and mini-muduo complete running examples, you can download them from GitHub. You can use git checkout v0.13 to switch to this version and browse this version online.
This version is a milestone version. You can use this version to learn how multithreading reads/writes network data through Io threads. The previous version v0.12 focuses on the basic knowledge, this article focuses on the three most important methods in multithreading logic: eventloop: runinloop/eventloop: queueinloop/eventloop: dopendingfunctors. Next, we will gradually introduce the details of the current revision. The three methods are placed in the last eventloop section.
1. Task class
This class is added to v0.13 and is a callback with parameters. Closure is used to replace boost: function and boost: bind in muduo. Why not use boost: function and boost: bind? As I have explained before, in order not to introduce new concepts, we can reduce the learning cost of mini-muduo. This task class is not quite universal (unlike blockingqueue, model implementation). It is only used in this project. task only supports two types of callback, the first is the callback without parameters. The caller only needs to implement a "Void run0 ()", and the second is the callback with two parameters, the called user implements "Void run2 (const string &, void *)". With the task class, all the places where asynchronous callback is required are implemented using it.
2 tcpconnection
A sendinloop method is added to move the implementation in the original send method to the sendinloop method, and the send method itself becomes an external interface packaging. Different policies are adopted based on the thread where the send method is called. If the thread that calls tcpconnection: Send happens to be an I/O thread, use Write to send data immediately (when the buffer is empty, of course ). If the thread that calls tcpconnection: Send is a work thread (that is, the background processing thread), only the information to be sent will be thrown to the asynchronous queue of eventloop through the task object, and then immediately return. Eventloop then calls back to the tcpconnetion: sendinloop method in the IO thread. This ensures that network I/O-related operations are only performed in the IO thread.
3 timerqueue
Not much changed, but asynchronous requests are encapsulated with tasks to ensure that all timer operations are performed in the IO thread. Because timerfd is a timer implemented by timerfd, and timerfd is monitored by epoll, it is easy to understand that all file descriptions of epoll monitoring must be placed in the IO thread.
4 echoserver
After receiving the task, it is not processed immediately. Instead, it is thrown into the thread pool through threadpool: addtask and processed using multiple threads. In the real processing callback, A simple simulation of a CPU-Consuming function (computing the Fibonacci series), the log shows that each task is assigned to different threads in the pool.
5 eventloop
1. The implementation of the wakeup method has been added in the previous version v0.12, but the call is commented out. The call point is in eventloop: queueinloop. This method is used to wake up the IO thread. Specifically, it is used to wake up epoll_wait In the IO thread. You only need to note that you should not forget to read this uint_64 in eventloop: handleread, otherwise, eventfd will be continuously stimulated to bring the program into an infinite loop.
2 eventloop: queueinloop method. This method is called queueloop in v0.12. To be consistent with the original muduo, this version is renamed. This method is used to add an asynchronous callback to the _ pendingfunctors of the queue to be executed. The first difference from v0.12 is that the current version locks _ pendingfunctors, which is easy to understand, this is because eventloop: queueloop is often called by other external non-io threads. The second modification is to add the wake-up () feature under certain conditions. Why does a single-threaded version not have this wake-up logic? Because all asynchronous calls in a single-threaded version are triggered after the loop starts and before dopendingfunctors (), you only need to insert the callback into the _ pendingfunctors array. However, in the multi-threaded queueinloop version, there are many entries. For example, in the following three cases, eventloop: queueinloop may be called.
Scenario 1 Io thread, In the imuduouser: onmessage callback, for example, in echoserver: onmessage.
Scenario 2 I/O thread, In the implementation body of dopendingfunctors () Execution task> dotask, such as echoserver: onwritecomplate.
Case 3: A non-I/O thread is in another thread in the thread pool.
In a single-threaded version, we can ignore case 3 and Case 2. Although this may happen, we simply assumed that the user would only add a task in onmessage, instead of adding tasks in the task callback. Therefore, the previous version is simplified here and does not require the wakeup () operation. Due to the consideration of these situations, the current version adds some condition judgments and wakeup () calls.
Pay special attention to the _ callingpendingfunctors variable. This variable is a bit obscure. I started to ignore it when writing a program, and later found that it is very important. If this variable is not found in Case 2 above, this will cause asynchronous calls to never be triggered!
3 eventloop: runinloop method. The new method added in this version is very similar to the queueinloop method. "runin" and "queuein" can be understood from the differences in names, when runinloop is called externally, it determines whether it is an IO thread. If it is in an IO thread, it immediately executes the callback in the task. Otherwise, the task is added to the asynchronous queue by calling queueinloop, waiting for subsequent calls.
4 eventloop: dopendingfunctors. This method is the same as the queueinloop method and is modified in two ways. First, the lock is required for multi-threaded vector operations, and the variable _ callingpendingfunctors is added, once again, this variable is very important.
At the end of this article, in order to better explain the logic of eventloop in a multi-threaded environment, I drew a time sequence diagram, which expresses "calling tcpconnection in a non-io thread :: send data "is a chain call caused by this action. This action requires three loops, involving four sub-calls.Green indicates that the code works in the IO thread,Red indicates that the code works in the work thread.(Working thread, the thread that actually processes computing tasks) in the original book, the multi-thread eventloop is located near page 294, but unfortunately, the author did not make a timing diagram for this process. Here is a makeup.