First, what is the thread pool? As the name implies, is to put a bunch of open threads in a pool unified management, is a thread pool.
Second, why use a thread pool, a request to it to apply for a thread, the request to dispose of the release thread? Yes, but if the time to create the thread and destroy the thread is longer than the thread is processing the request, and the request is a lot of time, our CPU resources are wasted on creating and destroying the thread, so this method is less efficient, so we can put a number of threads that have already been created together for unified management, If a request is made, we remove a thread from the thread pool to handle it and wait for the next task to be put back in the pool, the benefit of the thread pool is to avoid tedious creation and end-of-thread time, effectively leveraging CPU resources.
As I understand it, the role of the thread pool is similar to that of double buffering, which can accomplish the "filed" Action of task processing.
Finally, how can you create a model of a thread pool that typically requires the following three participants:
1, thread pool structure, it is responsible for managing multiple threads and providing the interface of the task queue
2. Worker threads, which are responsible for handling tasks
3. Task queue, store pending tasks
With three participants, the next question is how to make the thread pool work in a safe and orderly manner, using synchronization methods such as semaphores, mutexes, and conditional variables in POSIX. With these knowledge, we can create our own thread pool model, I found a more classic thread pool example on GitHub, interested can learn.
Original author GitHub address: Https://github.com/Pithikos/C-Thread-Pool
Data structures required by the thread pool:
(1), 0/1 semaphore, used to notify the thread when the task queue is non-empty, here is the semaphore implemented with mutexes and conditional variables, in fact, one of the implementations of POSIX semaphores is the mutex and condition variables
/**/struct bsem { pthread_mutex_t mutex; pthread_cond_t cond; int v; The value of V is not 0 or 1} bsem;
(2), identify the structure of the task, Prev point to the object is the previous task of the current task, where the pnext to identify more appropriate
/*Job*/typedefstructjob{structjob* prev;/*Pointer to previous job*/ void* (*function) (void* arg);/*function Pointer*/ void* ARG;/*function ' s argument*/} job;
(3), Work queue
/*Job Queue*/typedefstructjobqueue{pthread_mutex_t Rwmutex; /*used for queue r/w access*/Job*front;/*pointer to front of queue*/Job*rear;/*pointer to rear of queue*/Bsem*has_jobs;/*flag as binary semaphore*/ intLen/*Number of jobs in queue*/} jobqueue;
The mutex Rwmutex is used to synchronize read and write operations to the work queue, front is used to identify the first task in the work queue, rear is used to identify the last task in the work queue, Has_jobs is used to provide a provider of two-value semaphores, and Len represents the number of tasks in the current work queue.
(4), worker threads
/* Thread typedef struct thread{ int ID; /* friendly ID */ pthread_t Pthread; /* pointer to actual thread */ struct thpool_* thpool_p; /* access to Thpool */
ID identifies the number of threads, pthread represents the real thread ID created, and for each thread, it provides access to the thread pool.
(5), Thread pool structure
/*Threadpool*/typedefstructthpool_{Thread* * THREADS;/*Pointer to Threads*/ volatile intnum_threads_alive;/*Threads currently Alive*/ volatile intnum_threads_working;/*Threads currently working*/pthread_mutex_t Thcount_lock; /*used for thread count etc*/Jobqueue* JOBQUEUE_P;/*pointer to the job queue*/} Thpool_;
Threads can be seen as a pointer array, each pointer in the array points to a thread structure, num_threads_alive identifies how many working threads are in the thread pool, num_threads_working represents the number of threads in the current thread pool that are working , the mutex Thcount_lock provides mutually exclusive access to the thread pool's data, and it needs to collaborate with the task queue, so it also provides access to the task queue.
Workflow for the thread pool:
Initialize the thread pool, task queue, and worker threads, add tasks to the task queue, and wait for a thread on the condition variable (task queue to have a task) to wake up and remove the first task from the task queue to the thread execution--waits for all tasks in the task queue to finish. Closes the thread pool.
Understanding and simple implementation of Linux downline pool