Nginx thread pool source code parsing, nginx thread source code
I read some of the nginx thread pool code over the weekend and copied it as my version. There are some differences in implementation, but the basic structure is all excerpted.
Here we will share with you. If you understand my version, it proves that you understand the nginx thread pool.
This article only lists key data structures and APIs, and focuses on understanding the nginx thread pool design ideas. The complete code is in the final link.
1. Task Node
Typedef void (* CB_FUN) (void *); // The typedef struct task {void * argv; // parameters of the task function (before the task execution ends, to ensure that the parameter address is valid) CB_FUN handler; // task function (the return value must be 0 and a non-0 value is used to increase the thread and destroy the thread pool) struct task * next; // task link pointer} zoey_task_t;
Handler is the function pointer, which is the actual task function. argv is the parameter of this function, and next points to the next task.
2. task queue
Typedef struct task_queue {zoey_task_t * head; // queue header zoey_task_t ** tail; // queue end unsigned int maxtasknum; // maximum task limit unsigned int curtasknum; // number of current tasks} zoey_task_queue_t;
Head is the task queue header pointer, tail is the task queue tail pointer, maxtasknum is the maximum number of tasks in the queue, and curtasknum is the current number of tasks in the queue.
3. Thread Pool
Typedef struct threadpool {pthread_mutex_t mutex; // mutex lock pthread_cond_t cond; // condition lock wait tasks; // task queue unsigned int threadnum; // Number of threads unsigned int thread_stack_size; // thread stack size} zoey_threadpool_t;
Mutex is a mutex lock. cond is a conditional lock. Mutex and cond jointly guarantee the mutex collection or addition of thread pool tasks.
Tasks points to the task queue.
Threadnum indicates the number of threads in the thread pool.
Thread_stack_size indicates the thread stack size.
4. Start Configuration
// Configure the parameter typedef struct threadpool_conf {unsigned int threadnum; // Number of threads unsigned int thread_stack_size; // thread stack size unsigned int maxtasknum; // maximum task limit} zoey_threadpool_conf_t;
The STARTUP configuration struct is a parameter used to initialize the thread pool.
5. initialize the thread pool
First, check whether the parameter is valid, and then initialize mutex, cond, key (pthread_key_t ). Key is used to read and write global variables of the thread. This global variable controls whether the thread exits.
Finally, create a thread.
Zoey_threadpool_t * zoey_threadpool_init (runtime * conf) {zoey_threadpool_t * pool = NULL; int runtime = 0; int runtime = 0; pthread_attr_t attr; do {if (z_conf_check (conf) =-1) {// check whether the parameter is valid break;} pool = (zoey_threadpool_t *) malloc (sizeof (zoey_threadpool_t )); // apply for thread pool handle if (pool = NULL) {break;} // initialize the basic thread pool parameter pool-> threadnum = conf-> threadnum; pool-> thread_stack_size = Conf-> thread_stack_size; pool-> tasks. maxtasknum = conf-> maxtasknum; pool-> tasks. curtasknum = 0; z_task_queue_init (& pool-> tasks); if (z_thread_key_create ()! = 0) {// create a pthread_key_t to access the global variables of the thread. Free (pool); break;} if (z_thread_mutex_create (& pool-> mutex )! = 0) {// initialize the mutex lock z_thread_key_destroy (); free (pool); break;} if (z_thread_cond_create (& pool-> cond )! = 0) {// initialization condition lock z_thread_key_destroy (); z_thread_mutex_destroy (& pool-> mutex); free (pool); break;} if (z_threadpool_create (pool )! = 0) {// create the thread pool z_thread_key_destroy (); z_thread_mutex_destroy (& pool-> mutex); z_thread_cond_destroy (& pool-> cond); free (pool); break ;} return pool;} while (0); return NULL ;}
6. Add a task
First, apply for a task node. after instantiation, add the node to the task queue. Then, add the number of current task queues plus + and notify other processes of new tasks. Lock the entire process.
Int zoey_threadpool_add_task (zoey_threadpool_t * pool, CB_FUN handler, void * argv) {zoey_task_t * task = NULL; // apply for a task node and assign task = (zoey_task_t *) malloc (sizeof (zoey_task_t); if (task = NULL) {return-1;} task-> handler = handler; task-> argv = argv; task-> next = NULL; if (pthread_mutex_lock (& pool-> mutex )! = 0) {// lock free (task); return-1 ;}do {if (pool-> tasks. curtasknum> = pool-> tasks. maxtasknum) {// determines whether the number of tasks in the work queue has reached the limit break;} // inserts the task node to the task queue * (pool-> tasks. tail) = task; pool-> tasks. tail = & task-> next; pool-> tasks. curtasknum ++; // notify the blocked thread if (pthread_cond_signal (& pool-> cond )! = 0) {break;} // unlock pthread_mutex_unlock (& pool-> mutex); return 0 ;}while (0); pthread_mutex_unlock (& pool-> mutex ); free (task); return-1 ;}
7. Destroy the thread pool
The thread pool is actually added to the task queue, but the added task is to let the thread exit. The z_threadpool_exit_cb function will set the lock to 0 and exit the thread. If the lock is 0, the thread will exit.
It has exited and then exited the next thread. After exiting the thread, all resources are released.
Void zoey_threadpool_destroy (zoey_threadpool_t * pool) {unsigned int n = 0; volatile unsigned int lock; // The z_threadpool_exit_cb function will cause the corresponding thread to exit; n ++) {lock = 1; if (zoey_threadpool_add_task (pool, z_threadpool_exit_cb, & lock )! = 0) {return;} while (lock) {usleep (1) ;}} z_thread_mutex_destroy (& pool-> mutex); z_thread_cond_destroy (& pool-> cond ); z_thread_key_destroy (); free (pool );}
8. Add a thread
It's easy to generate a thread and the number of threads plus +. Lock.
int zoey_thread_add(zoey_threadpool_t *pool){ int ret = 0; if (pthread_mutex_lock(&pool->mutex) != 0){ return -1; } ret = z_thread_add(pool); pthread_mutex_unlock(&pool->mutex); return ret;}
9. Change the maximum task limit of the task queue
When num = 0, the number of threads is set to be infinitely large.
Void zoey_set_max_tasknum (zoey_threadpool_t * pool, unsigned int num) {if (pthread_mutex_lock (& pool-> mutex )! = 0) {return-1;} z_change_maxtask_num (pool, num); // change the maximum task limit pthread_mutex_unlock (& pool-> mutex );}
10. Example
Int main () {int array [10000] = {0}; int I = 0; zoey_threadpool_conf_t conf = {5, 0, 5 }; // instantiate the startup parameter zoey_threadpool_t * pool = zoey_threadpool_init (& conf); // initialize the thread pool if (pool = NULL) {return 0 ;}for (; I <10000; I ++) {array [I] = I; if (I = 80) {zoey_thread_add (pool); // Add the thread zoey_thread_add (pool );} if (I = 100) {zoey_set_max_tasknum (pool, 0); // change the maximum number of tasks 0 to no upper limit} while (1) {if (zoey_threadpool_add_task (pool, testfun, & array [I]) = 0) {break;} printf ("error in I = % d \ n", I) ;}} zoey_threadpool_destroy (pool ); while (1) {sleep (5);} return 0 ;}
11. Source Code
https://github.com/unlikewashface/zoey_threadpool.git