C++ performance shocking group problem

What is the shock group problem?

Insert picture description here

Scene 1: Six little birds rest on the wire, all waiting for food.
Insert picture description here
Scenario 2: We throw a small worm to the birds as their food.
Insert picture description here
Scene 3: When the six little birds saw food coming, they all stopped resting and flew together to grab the food.
Insert picture description here
Scene 4: In the end, only one bird (bird4) can eat the food, and the other birds return to the wire helplessly and sadly to continue to rest.
Insert picture description here
Our little scene above is actually a real-world frightening problem. Obviously only one small bug has arrived, but the 6 little birds have to stop resting to grab food. Except for the bird that grabbed food, the others could not grab food. The bird needs to fly back to rest again. For this part of the bird, it is unnecessary to waste a lot of energy.

So what is it like in a computer? In fact, similar to the above scenario, multiple threads (or processes) wait for the arrival of an event at the same time and prepare to process the event. When the event arrives, all threads (or processes) waiting for the event are awakened, but only one thread can finally The right to handle the event can be obtained, and all other threads fall back into sleep to wait for the next event. This kind of thread is frequently awakened without actually processing the event, which leads to unnecessary waste of the CPU, which is called the "shock group problem" in computers.

The scene of the shock group problem

The accept call in the system API before the Linux2.6 kernel version

Before the Linux 2.6 kernel version, when the accept function in multiple threads simultaneously monitors the same listenfd, if the listenfd becomes readable, the system will wake up all threads (or processes) that use the accept function to wait for listenfd. But in the end, only one thread can accept the call and return success, the accept function calls of other threads return EAGAIN error, and the thread returns to the waiting state. This is the shock group problem caused by the accept function. However, after Linux 2.6, the kernel has solved the shock group problem of the accept function. When the kernel receives a connection, it will only wake up the first thread (or process) on the waiting queue, thereby avoiding the shock group problem.

Shocking group problem in epoll function

If we use multi-threaded epoll to monitor the same fd, when the fd event arrives, the kernel will wake up all epoll threads, thus causing a shock group problem. Why can't the kernel solve the epoll shocking group problem like it solves the accept problem? The kernel can solve the shock group problem in the accept call, because the kernel clearly knows that only one thread can succeed in the accept call, and other threads must fail. For the epoll call, the kernel does not know how many threads need to process the event, so it can only wake up all threads.

Shocking group problem in thread pool

In the actual application development, in order to avoid the frequent creation and destruction of threads, we generally establish a thread pool for concurrent processing, and the most classic model of thread pool is the producer-consumer model, which contains a task queue, when the queue is not empty At that time, the threads in the thread pool take out tasks from the task queue for processing. Generally, condition variables are used for processing. When we put tasks in the task queue, we need to wake up the waiting threads to process the tasks. If we use the function notify_all() in the C++ standard library to wake up the threads, all threads will be Wake up, and then finally only one thread can obtain the processing power of the task, and other threads fall into sleep here, thus causing the shock group problem.

Solutions to the shock group problem

For the solution to the surprise group problem of epll function calls, you can refer to the Nginx solution. Before multiple processes add listenfd to epoll, first try to obtain a global accept_mutex mutex. Only the process that obtains the lock can add listenfd to In epoll, when a network connection event arrives, only the thread containing listenfd in epoll will be awakened and process the network connection event. So as to solve the shock group problem in epoll call.

Regarding the shock group problem in the thread pool, we need to look at it separately. Sometimes the business requirement is to wake up all threads. At this time, using notify_all() to wake up all threads cannot be called "the shock group problem", because the CPU is not useless. Consumption. For situations where only one thread needs to be awakened, we need to use the notify_one() function instead of notify_all() to wake up only one thread to avoid the cluster problem.

Insert picture description here
Insert picture description here

Finally, the editor recommends my own Linux and C/C++ technical exchange group: [960994558] I have compiled some learning books and video materials that I think are better for sharing (including C/C++, Linux, Nginx, ZeroMQ, MySQL, Redis, fastdfs, MongoDB, ZK, streaming media, CDN, P2P, K8S, Docker, TCP/IP, coroutine, DPDK, etc.), you can add it yourself if you need it! ~

Guess you like

Origin blog.csdn.net/weixin_52622200/article/details/111476998