In the design process of various business solutions, the efficiency of server processing tasks often determines the success or failure of the scheme. Multithreading task is the main means to improve server efficiency, it improves the utilization of server resources, so that tasks can be processed concurrently. However, if the server is dealing with the task of lightweight, high frequency, then the creation and destruction of threads will be very frequent, and the system used to handle the creation and destruction of threads accounted for a significant proportion of the cost, but reduced the efficiency of the system. Thread pooling technology can reduce the impact of frequent thread creation and destruction on system performance.
A thread pool is a technique for creating threads beforehand. The thread pool creates a number of (N1) threads and puts them in the idle queue before the task arrives. These threads are in a blocking (suspended) state, do not consume the CPU, but occupy a small amount of memory space. When the task arrives, the buffer pool selects an idle thread and runs the task into this thread. When N1 threads are working on a task, the buffer pool automatically creates a number of new threads to handle more tasks. When the system is more idle, most of the threads have been paused, the thread pool automatically destroys some of the threads and reclaims the system resources.
The design of the universal thread buffer pool is not only to realize the above functions, but also to consider the portability of the design and reduce the duplication of development. The key points to be considered in the design are:
The universality of the Task object;
Thread creation and destruction policies;
Assignment policy for the task.
Analysis and Design
1, the universality of the Task object
Different business solutions have their own unique task processing methods, and the division of tasks varies greatly. In order to achieve a certain degree of commonality when dealing with task objects, the design of task objects must have nothing to do with the processing logic of actual tasks. From a task execution point of view, a task is nothing more than a process that is executed one or more times, so that the task interface can be defined:
class Task
{
public:
Task();
virtual ~Task();
virtual bool run() = 0;
};
A task class is the base class for all task classes, where the pure virtual function run () is the portal to the task process, where the worker thread begins to perform the process of the task as it processes the task. When you design a new task, you simply inherit the task interface, and the new task can be put into the thread pool for execution.
The creation, execution, and destruction of tasks are designed to:
(1) The task is created when it is needed. The creation of a task creates a specific task object dynamically through the new operation, and then passes in to the thread pool, which automatically assigns threads to perform this task by the thread pool.
(2) The execution of the task is decided by itself. It is impossible to predict when an unknown task will be executed, and it must be determined by the task itself. This policy is implemented by the return value of the Task::run (). When a worker thread performs a task, if the return value is true to indicate that the task has finished executing, the task is destroyed with the delete operation, and if the return value is False, indicating that the task needs to be performed without completion, continue with the task.
This strategy allows you to design a new task-processing process instead of requiring too much care about the interface specification of the task, simply initialize the resources in the constructor of the new task class, reclaim the resources in the destructor of the new task class, and implement the main processing logic in the run () method. The new task class can then be executed in the thread pool.