The weekend read the Nginx thread pool part of the code, conveniently copied, written in its own version. There are differences in the implementation of some places, but the basic structure of all excerpts.
Share it here. If you read my version, you'll see that you understand the nginx thread pool.
This article only lists the 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 nodes
typedef void (*cb_fun) (void *);
Task structure
typedef struct task
{
void *argv//Task function parameters (to ensure that the parameter address is valid before the task execution is completed)
cb_fun handler;// Task function (The return value must be 0 non 0 value as an addition thread, and destroy the thread pool)
struct task *next;//tasks chain pointer
}zoey_task_t;
Handler is the function pointer, is the actual task function, argv is the parameter of the function, and next points to the next task.
2. Task Queue
typedef struct TASK_QUEUE
{
zoey_task_t *head;//Queue head
zoey_task_t **tail; Queue tail
unsigned int maxtasknum;//MAX task limit
unsigned int curtasknum;//Current number of tasks
}zoey_task_queue_t;
Head is the task queue header pointer, tail is the task queue tail pointer, maxtasknum limit the maximum number of queues for the queue, Curtasknum is the number of current tasks for the queue.
3. Thread pool
typedef struct ThreadPool
{
pthread_mutex_t mutex;//Mutex lock
pthread_cond_t cond; Conditional lock
zoey_task_queue_t tasks;//task Queue
unsigned int threadnum;//number of threads
unsigned int thread _stack_size; Thread stack size
}zoey_threadpool_t;
The mutex cond the mutex as a conditional lock. Mutexes and cond together ensure that the thread pool task is mutually exclusive or added.
Tasks point to task queues.
Number of threads threadnum to the thread pool
Thread_stack_size for thread stack size
4. Start configuration
Configuration parameters
typedef struct THREADPOOL_CONF
{
unsigned int threadnum; Thread Count
unsigned int thread_stack_size;//thread stack size
unsigned int maxtasknum;//max task limit
}zoey_threadpool_ conf_t;
The startup configuration structure is some of the parameters when the thread pool is initialized.
5. Initializing the thread pool
First check that the arguments are legitimate, and then initialize Mutex,cond,key (pthread_key_t). Key is used to read and write the thread global variable, which controls whether the thread exits.
Finally, the thread is created.
zoey_threadpool_t* zoey_threadpool_init (zoey_threadpool_conf_t *conf) {zoey_threadpool_t *pool = NULL;
int Error_flag_mutex = 0;
int error_flag_cond = 0;
pthread_attr_t attr;
do{if (z_conf_check (conf) = = 1) {//Check whether the parameter is legal break;
Pool = (zoey_threadpool_t *) malloc (sizeof (zoey_threadpool_t));//request thread pool handle if (pool = = NULL) {break;
}//Initialize thread pool basic parameters 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 thread global variable.
Free (pool);
Break
} if (Z_thread_mutex_create (&pool->mutex)!= 0) {//Initialize mutual-exclusion 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 (&Amp;pool->mutex);
Free (pool);
Break
} if (Z_threadpool_create (pool)!= 0) {//Create 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 you request a task node, instantiate it, add the node to the task queue, and add the current task queue number + + and notify other processes of a new task. Lock the whole process.
int Zoey_threadpool_add_task (zoey_threadpool_t *pool, Cb_fun handler, void* argv)
{zoey_task_t *task
= null;< c3/>//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) {//Determine whether the number of tasks in the work queue reaches the limit break
;
}
Insert the end of the task node into the task queue
* (pool->tasks.tail) = task;
Pool->tasks.tail = &task->next;
pool->tasks.curtasknum++;
Notifies 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. Destroying the thread pool
Destroying the thread pool is actually adding tasks to the task queue, except that the task is to have the thread quit. The Z_THREADPOOL_EXIT_CB function will lock a 0 back thread and lock is 0 to indicate that the thread
Has exited, then exits the next thread. Exit the thread and release all resources.
void Zoey_threadpool_destroy (zoey_threadpool_t *pool)
{
unsigned int n = 0;
volatile unsigned int lock;
The Z_THREADPOOL_EXIT_CB function causes the corresponding thread to exit for
(; n < pool->threadnum; 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 simple, then generate a thread and the number of threads + +. 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 Task Queue maximum task limit
Set the number of threads to infinity when num=0.
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 Maximum task limit
pthread_mutex_unlock (&pool->mutex);
}
10. Using the example
int main ()
{
int array[10000] = {0};
int i = 0;
zoey_threadpool_conf_t conf = {5,0,5}; Instantiate startup parameter
zoey_threadpool_t *pool = Zoey_threadpool_init (&conf);//Initialize thread pool
if (pool = NULL) {return
0;
}
for (; i < 10000; i++) {
array[i] = i;
if (i = =) {
zoey_thread_add (pool);//Add thread
zoey_thread_add (pool);
}
if (i = =) {
zoey_set_max_tasknum (pool, 0);//Change the maximum number of tasks 0 is not 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
Thread pooling can be more useful, such as putting a connection to a line Cheng Chili. Nginx's asynchronous plus Lua coprocessor is a very good combination, and now that the thread pool is available, the thread pool Ga will be another choice. All in all, it is very good news to make Nginx development very simple if it is guaranteed to be performance.