1. What is a thread pool
A thread pool is a collection of threads that have several threads that are typically used to perform large, relatively short-lived tasks. If a task executes for a long time, it is not suitable to be processed in the thread pool, for example, when the execution time of a task is consistent with the life cycle of the process, then the processing of this thread is not necessary to be dispatched in the pool of threads, with a normal thread.
The number of threads in the thread pool is too small to reduce the concurrency of the system, and too much of it increases the overhead of the system. Generally, the number of threads in a thread pool is related to the type of thread, and the type of thread is divided into
1. computationally intensive tasks;
2. I/O intensive tasks.
Compute-intensive tasks are CPU resources, rarely interrupted by external events, the number of CPUs is certain, so the number of concurrent is certain, so the number of threads equals the number of CPUs is the most efficient.
I/O intensive tasks mean that I/O may be interrupted during execution, that is, the thread will be suspended, and the number of threads should be greater than the number of CPUs.
The essence of thread pool is the application of producer and consumer model. The producer thread adds a task to the task queue, and once the queue has a task, if the thread pool has an idle thread, wakes up the idle thread to perform the task, if there are no idle threads, and the number of threads does not reach the threshold (the maximum of threads in the thread pool), a new thread is created to perform the task.
The number of threads in the thread pool can be dynamically increased when the task is increased until a threshold is reached. This threshold is the maximum number of threads in the thread pool.
The thread pool in the thread pool can be destroyed dynamically when the task finishes executing.
2. Implementation of thread pool
I am in the Ubuntu system under the C language Program (Portal: GitHub), this is a very simple thread pool implementation, the code volume of about 300 lines, just explain how the thread pool works, I will finally give the idea of expanding the thread pool, so that it becomes a C/s architecture, The thread pool for socket communication.
At present, there are two points of knowledge used:
1. pthread_mutex_t: mutual exclusion lock;
2. pthread_cond_t: Condition variable;
Mutex and condition variables to use, I in another blog (click Open link) in the use of methods, interested in children's shoes can go to see ~
Our small thread pool consists of five files:
1. Condition.h: The mutex and condition variables are combined together to form a conditional structure, Condition.h is the declaration of this structure;
2. CONDITION.C: Corresponding to the condition.h, defines the function of the operating condition structure;
3. Threadpool.h: Contains two structures, one is the thread control block (TCB), the other is the threads pool structure, and three functions: initializing the thread pool, destroying the thread pool, adding tasks to the thread pools;
4. Realization of threadpool.c:threadpool.h;
5. MAIN.C: main function;
In addition, I use Autotool to compile the program, so there are two script files:
1. makefile.am: Define dependencies between files;
2. build.sh: Compile script;
Let's dive into the code to understand the thread pool ~
Condition.h, in order to achieve thread synchronization, it is common practice to use mutual exclusion (pthread_mutex_t) and condition variables (pthread_cond_t) together, the best way is to combine the two into a structure, pthread_mutex_ T and pthread_cond_t belong to the Pthread.h header file:
/******************************************************************** Copyright (c) Chen gonghao* All rights reserved.** [Email protected]******************************************************************/#ifndef _ Condition_h_#define _condition_h_#include <pthread.h>/* encapsulates a mutex and a condition variable into a struct */typedef struct condition{pthread_ mutex_t Pmutex; pthread_cond_t Pcond;} condition_t;/* Initialize struct */int condition_init (condition_t* cond);/* Get mutex in struct */int condition_lock (condition_t* cond) /* Release the mutex in the struct */int condition_unlock (condition_t* cond);/* Causes the consumer thread to wait on the condition variable */int condition_wait (condition_t* cond); * Make the consumer thread wait on the condition variable, abstime: Wait timeout */int condition_timedwait (condition_t* cond, const struct timespec* abstime);/* producer Thread notification wait The consumer thread on the condition variable */int condition_signal (condition_t* cond);/* The producer thread broadcasts */int condition_broadcast to the consumer thread waiting on the condition variable (condition _t* cond);/* Destroy Structure */int Condition_destroy (condition_t* cond); #endif
CONDITION.C, there are 8 functions for the conditional structure, CONDITION.C is the definition of these 8 functions, very simple, is to call the Pthread.h header file in the library function.
/******************************************************************** Copyright (c) Chen gonghao* All rights reserved.** [email protected]******************************************************************/#include] Condition.h "int condition_init (condition_t* cond) {int status; if (status = Pthread_mutex_init (&cond->pmutex, NULL)) {return status; } if (status = Pthread_cond_init (&cond->pcond, NULL)) {return status; } return 0;} int Condition_lock (condition_t* cond) {return pthread_mutex_lock (&cond->pmutex);} int Condition_unlock (condition_t* cond) {return pthread_mutex_unlock (&cond->pmutex);} int condition_wait (condition_t* cond) {return pthread_cond_wait (&cond->pcond, &cond->pmutex);} int condition_timedwait (condition_t* cond, const struct timespec* abstime) {return pthread_cond_timedwait (&cond- >pcond, &cond->pmutex, abstime);} int condition_sIgnal (condition_t* cond) {return pthread_cond_signal (&cond->pcond);} int Condition_broadcast (condition_t* cond) {return pthread_cond_broadcast (&cond->pcond);} int Condition_destroy (condition_t* cond) {int status; if (status = Pthread_mutex_destroy (&cond->pmutex))) {return status; } if (status = Pthread_cond_destroy (&cond->pcond))) {return status; } return 0;}
Pthreadpool.h: Contains two structs, Thread control block (TCB) task_t and thread pool structure threadpool, and three actions against the thread pool: initializing the thread pool, destroying the thread pools, adding tasks to the thread pooling.
/******************************************************************** Copyright (c) Chen gonghao* All rights reserved.** [email protected]******************************************************************/#ifndef _THREAD_ Pool_h_#define _thread_pool_h_#include "condition.h"/* Thread control Blocks (task control block), organizing TCB */typedef struct task{v as a one-way list OID * (*run) (void* Arg); The execution function of the thread is void *arg; parameter of the execution function struct tast* next; Point to the next TCB} task_t;/* thread pool struct */typedef struct threadpool{condition_t ready;//Conditional variable struct task_t* first;//TCB list The head pointer task_t* last; The trailing pointer int counter of the TCB list; The total number of TCB int idle; The number of idle TCB int max_threads; Maximum number of threads int quit; Thread pool Destroy flag} threadpool_t;/* Initialize thread pool */void threadpool_init (threadpool_t* pool, int threads);/* Add task to thread pools */void thread Pool_add_task (threadpool_t* pool, void* (*run) (void* Arg), void* arg);/* Destroy thread pool */void Threadpool_destroy (Threadpoo l_t* pool); #endif
THREADPOOL.C, a total of four functions, operation on the thread pool.
/******************************************************************** Copyright (c) Chen gonghao* All rights reserved.** [email protected]******************************************************************/#include] Threadpool.h "#include <errno.h> #include <time.h>/* thread entry function */void* thread_runtime (void* arg) {struct times PEC abstime; int timeout; Get the thread pool object threadpool_t* pool = (threadpool_t*) arg; while (1) {timeout = 0; /************************** enters the critical zone ***********************/condition_lock (&pool->ready); Number of idle threads plus 1 + + Pool->idle; If the line threads is empty and the thread pool is running, then the thread waits for the task to arrive while (Pool->first = = NULL && Pool->quit = = 0) {p rintf ("Thread 0x%x is waiting\n", (int) pthread_self ()); Clock_gettime (Clock_realtime, &abstime); Abstime.tv_sec + = 2; int status = Condition_timedwait (&pool->ready, &abstime); if ( Status = = Etimedout) {printf ("Thread 0x%x is wait timed out\n", (int) pthread_self ()); Timeout = 1; break; }}//If the thread waits for timeout if (timeout && pool->first = = NULL) {--pool->counter;//Then Number of threads reduced by 1 condition_unlock (&pool->ready); Release the mutex lock break; Jump out while, note that after break, the thread entry function completes, the thread will no longer exist}//thread to get the task-pool->idle; Thread pool idle number minus 1 if (pool->first! = NULL) {task_t* t = Pool->first;//Remove TCB from List head POOL-&G T;first = T->next; Pointing to the next TCB//execution takes a certain amount of time, so unlock//So that the producer can join the task in the linked list//and other consumers can wait for the task Condition_unlock (&A Mp;pool->ready); T->run (T->ARG); The callback function that executes the task free (t); The task is executed and the TCB Condition_lock (&pool->ready) is destroyed; }//quit = = 1 description to destroy the thread pool if (pool->quit && pool->first = = NULL) {--Pool->cOunter; if (Pool->counter = = 0) {condition_signal (&pool->ready);//wake-up wait on the condition variable on the main thread} Condition_unlock (&pool->ready); break; } condition_unlock (&pool->ready); /************************** Exit critical section ***********************/} return NULL; /* Initialize thread pool */void threadpool_init (threadpool_t* pool, int threads) {condition_init (&pool->ready);//Initialize condition variable Structural body Pool->first = NULL; Sets the thread-linked header pointer pool->last = NULL; Set line threads tail pointer pool->counter = 0; Set the thread pool when the number of Pool->idle = 0; Sets the thread pool current number of idle threads pool->max_threads = threads; Set thread pool maximum thread count pool->quit = 0; Quit = 0, thread pool running state; quit = 1, thread pool destroy state}/* add threads to thread pools */void threadpool_add_task (threadpool_t* pool, void* (*run) (void* arg ), void* Arg) {task_t* NewTask = (task_t*) malloc (sizeof (task_t));//Create thread control block Newtask->run = run;//Set Line The callback function of the process newtask->arg = arg; Sets the parameter of the callback function Newtask->next = NULL; The newly added thread will be added to the tail of the list/************************** into the critical section ***********************/Condition_lock (&pool->ready); Get the Mutex//Add the newly created TCB to the thread chain list if (Pool->first = = NULL) {//If the line threads is empty, the TCB acts as the head of the list Pool->first = n Ewtask; } else {//If the line threads is not empty, add to the tail of the list pool->last->next = NewTask; } pool->last = NewTask; Modify the end of the list pointer//If there are idle threads, then wake the idle thread if (Pool->idle > 0) {condition_signal (&pool->ready); Notifies idle threads waiting on a condition variable} else if (Pool->counter < pool->max_threads) {//If no idle thread is available and the current number of threads is less than the capacity of the thread pool, we create a thread pthread_t tid; Pthread_create (&tid, NULL, thread_runtime, pool); Specifies that the starting function for the new thread is Thread_runtime, and the thread pool is passed to Thread_runtime + + Pool->counter; } condition_unlock (&pool->ready); Release the mutex/************************** exit the critical section ***********************/}/* destroy the thread pool */void Threadpool_destroy (threadpool_t* p OOL) {if (pool->quit) {REturn; }/************************** enters the critical zone ***********************/condition_lock (&pool->ready); Set exit flag to True pool->quit = 1; If a thread is running in the thread pool, then we need to wait for the thread to finish and then destroy if (Pool->counter > 0) {if (Pool->idle > 0) {C Ondition_broadcast (&pool->ready); } while (Pool->counter > 0) {condition_wait (&pool->ready);//main thread (main function is thread) will wait in the condition Variable}} condition_unlock (&pool->ready); /************************** Exit critical section ***********************///Destroy condition variable Condition_destroy (&pool->ready);}
And finally the main function, main.c.
/******************************************************************** Copyright (c) Chen gonghao* all Rights reserved.** [email protected]******************************************************************/# Include "threadpool.h"/* define thread pool maximum number of threads */#define MAX_POOL_SIZE3/* callback function for each task */void* MyTask (void* Arg) { printf ("Threa D 0x%x is working on task%d\n ", (int) pthread_self (), * (int*) arg); Sleep (1); Free (ARG); return NULL;} int main (void) { threadpool_t pool;//define a thread pool variable threadpool_init (&pool, max_pool_size);//Initialize thread pool // To add 10 tasks to the thread pool, each task's handler function is mytask for (int i = 0; i < x + + i) { int* arg = (int*) malloc (sizeof (in t)); *arg = i; Threadpool_add_task (&pool, MyTask, arg); } Threadpool_destroy (&pool); Destroy thread pool return 0;}
Draw a diagram to understand how the thread pool works:
It can be found that the producer's code is mainly located in the Thread_add_task () function, and the consumer's code is mainly located in the Thread_runtime () function.
Careful children's shoes may have found: Thread_runtime () only while loop, three consumers simultaneously execute while loop, it is impossible to ensure that the loop ordered from the production line to take the task, the task to temporarily how to ensure that three consumers do not have a scramble behavior? This is the function of the condition variable, when the consumer found that the production line is empty, then sleep on the condition variable, the key code sleeping on the condition variable is as follows:
int status = Condition_timedwait (&pool->ready, &abstime);
If you sleep overtime, you will automatically destroy it.
When the task comes to a temporary, the producer is obliged to inform the sleeping consumer, wake the latter, get up Hi ~, the key code is as follows:
Condition_signal (&pool->ready); Wake up the main thread waiting on the condition variable
The results of the implementation are as follows:
I set the thread pool capacity at 2 and the number of tasks to 10, so there are 2 threads that handle 10 tasks.
From what can be found, two threads are 0xaa1de700 and 0xaa9df700 respectively, these two threads process 0 to 9th tasks sequentially, no task processing, then enter the waiting state (is waiting), the result of waiting time-out is automatically destroyed, and then the main thread exits, the program ends.
3. Extension thread pool
The above is a minimal thread pool implementation, which is just a toy to understand the implementation of the thread pool. But we can expand the small toys into a model with a kind of thread pool: c/S architecture, socket communication, the following is the expansion of ideas, all the expanded code is:Simple_thread_pool/extension/。
Client:
1. To seal a socket, that is, to package the native socket into a class, I have done this step, the code in:Simple_thread_pool/extension/Osssocket/;
2. Make a command factory class (Commandfactory), according to the input of the client, produce a series of command objects;
3. Client class, which contains the socket class and commandfactory;
Server-side:
The server side is basically an extension of the small thread pool described in this article.
1. Add a listener function to the main function: TcpListener ();
2. The Code of TcpListener () is mainly composed of the Code of Threadpool_add_task () and Threadpool_runtime ();
A lot of basic software, such as databases, servers, the bottom is running a thread pool, if there is time, I will expand the small thread pool, and then write a blog to introduce technology implementation.
Implementation of a simple thread pool