Nginx线程池


  • 1.前言
  • 2.为什么使用线程池
  • 1.为什么要使用多线程
  • 2.为什么要使用线程池技术
  • 3.线程池实现思路
  • 4.线程池数据结构
  • 1.任务队列任务节点
  • 2.任务队列
  • 3.线程池
  • 5.线程池代码解析
  • 6. Nginx 完整代码


1.前言

最近学习了nginx的线程池,并对nginx的线程池做了一个简单的模仿实现,用博客记录一下学习的心得。

2.为什么使用线程池

1.为什么要使用多线程

我们先假设在某个应用场景中有很多个不一样的待处理的任务,有的任务能够很快处理完,有的则需要很长时间,甚至可能阻塞。阻塞的原因可能为:

  1. 读取文件,但文件尚未缓存,从硬盘中读取较为缓慢
  2. 不得不等待获取某个资源:
    硬件驱动
    互斥锁
    等待同步方式调用的数据库响应
    网络上的请求和响应

如果我们使用传统方式下的单进程或单线程,那效率肯定很低。所以我们使用多线程,以便提高效率。

2.为什么要使用线程池技术

在多线程的基础上为什么还要使用线程池技术呢,这是因为线程的创建与销毁是需要资源与时间的,在处理简单的请求时,可能处理请求所花费的时间比创建销毁线程的时间还少。也就是说创建与销毁线程所花费的资源太多我们应尽量减少这种资源浪费。

在上述需要下线程池技术便应运而生。

3.线程池实现思路

nginx启用多线程 nginx线程池_nginx启用多线程

完整的线程池由两个部分组成,分别是存放任务的任务队列,用来放置需要处理的任务,以及处理任务的线程池,由多个线程组成,用来处理任务。

在线程池创建后,启动多个线程,绑定同一个处理函数,该处理函数的功能就是循环提取任务队列中的任务进行处理。在提取任务时,需要对任务队列进行操作,但因为任务队列是共享资源,所以线程在操作它时,需要进行线程间的同步互斥。这一点通过互斥锁与条件变量完成。

4.线程池数据结构

1.任务队列任务节点

线程池的任务队列被组织成一个单链表的形式。

struct thread_task_s {
    
    thread_task_t       *next;    //指向下一个节点的指针
    int                 id;      //任务节点 ID
    void                *ctx;     //指向参数首地址的指针
    void               (*handler)(void *data);     //函数指针,指向当前节点要执行的函数
};

2.任务队列

通过 thread_pool_queue_t 结构体持有任务队列。

typedef struct {
	
    thread_task_t        *first;     //指针任务队列头节点
    thread_task_t        **last;     //指针任务队列尾节点
} thread_pool_queue_t;

3.线程池

通过 struct thread_pool_s 结构体持有线程池。

struct thread_pool_s {
    
    pthread_mutex_t        mtx;        //互斥量
    thread_pool_queue_t   queue;       //任务队列
    int                   waiting;     //等待执行的任务数
    pthread_cond_t        cond;       //条件变量

    char                  *name;       //线程池名字
    int                   threads;     //线程池线程数量
    int                   max_queue;   //任务队列最大任务数量
};

5.线程池代码解析

代码逻辑为:

任务队列中存放了将要执行的函数的函数指针,启动多个线程循化获取任务队列节点并取出该函数指针执行函数,执行完,该节点出队。以此循化直到任务队列为空,线程结束。程序结束。

1.main.c文件解析(详细解析见注释)

#include "thread_pool.h"

void text(void* data){                                           //线程池将要处理的函数text

     int* tmp=(int*)data;

     printf("-----------------%d--------------\n",*tmp);

     return;
}

void text1(void* data){                                          //线程池将要处理的函数text1

     int* tmp=(int*)data;

     printf("-----------------%d--------------\n",*tmp);

     return;
}

void text2(void* data){                                           //线程池将要处理的函数text2

     int* tmp=(int*)data;

     printf("-----------------%d--------------\n",*tmp);

     return;
}

void text3(void* data){                                           //线程池将要处理的函数text3

     int* tmp=(int*)data;

     printf("-----------------%d---------------\n",*tmp);

     return;
}

int main(void){

    thread_pool_t* pool;                         //线程池指针

    pool= thread_pool_init();               //初始化线程池

    thread_task_t* task=thread_task_alloc(sizeof(int));              //创建任务task
    thread_task_t* task1=thread_task_alloc(sizeof(int));             //创建任务task1
    thread_task_t* task2=thread_task_alloc(sizeof(int));             //创建任务task2
    thread_task_t* task3=thread_task_alloc(sizeof(int));             //创建任务task3

    task->handler=text;                      //指定任务携带函数text
    task1->handler=text1;
    task2->handler=text2;
    task3->handler=text3;

    *((int*)(task->ctx))=666;                  //指定任务携带参数ctx
    *((int*)(task1->ctx))=777;
    *((int*)(task2->ctx))=888;
    *((int*)(task3->ctx))=999;

    thread_task_post(pool,task);               //加入到任务队列
    thread_task_post(pool,task1);
    thread_task_post(pool,task2);
    thread_task_post(pool,task3);

    sleep(5);

    thread_pool_destroy(pool);               //销毁线程
    return 0;
}

2.thread_pool.h文件(见注释)

#ifndef _THREAD_POOL_H_INCLUDED_
#define _THREAD_POOL_H_INCLUDED_


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/types.h>
#include <pthread.h>
#include <errno.h>
#include <string.h>


#define MAX_THREAD_COUNT 5
#define MAX_TASK_QUEUE 65535


typedef struct thread_task_s{

       struct thread_task_s*   next;
       int                       id;
       void*                    ctx;
       void                    (*handler)(void* data);
}thread_task_t;


typedef struct{

        thread_task_t*  first;
        thread_task_t**  last;
}thread_pool_queue_t;


typedef struct thread_pool_s{

       pthread_mutex_t         mtx;
       thread_pool_queue_t     queue;
       int                     waiting;
       pthread_cond_t          cond;

       char*                   name;
       int                     threads;
       int                     max_queue;

}thread_pool_t;


thread_task_t*  thread_task_alloc(int size);
int thread_task_post(thread_pool_t* tp,thread_task_t* task);
thread_pool_t* thread_pool_init();
void thread_pool_destroy(thread_pool_t* tp);
void thread_pool_cycle(thread_pool_t* tp);

3.thread_pool.c文件(见注释)

#include "thread_pool.h"

thread_pool_t* thread_pool_init(){      //初始化线程池

       printf("----------thread_pool_init---------\n");

       thread_pool_t* tp=NULL;
       pthread_t tid;         //互斥量参数
       pthread_attr_t  attr;

       tp=calloc(1,sizeof(thread_pool_t));   //给线程池分配内存
       if(!tp){

          printf("init faile!\n");
          exit(-1);
       }

       tp->threads=MAX_THREAD_COUNT;    //初始化参数
       tp->max_queue=MAX_TASK_QUEUE;

       tp->queue.first=NULL;   //初始化线程池任务队列
       tp->queue.last=&tp->queue.first;

       if(pthread_mutex_init(&tp->mtx,NULL)){  //初始化互斥量

          free(tp);
          exit(-2);
       }

       if(pthread_cond_init(&tp->cond,NULL)){  //初始化条件变量

          free(tp);
          pthread_mutex_destroy(&tp->mtx);
          exit(-3);
       }


       int i;
       for(i=0;i<tp->threads;++i){   //创建线程

           int err=pthread_create(&tid, NULL, thread_pool_cycle, tp);
           if(err){

              printf("thread create faile!\n");
              free(tp);
              exit(-4);
           }
       }

       return tp;
}

int thread_task_post(thread_pool_t* tp,thread_task_t* task){   //将任务节点加入任务队列

    printf("-------thread_task_post---------\n");

    if(!tp || !task){

       printf("pointer is NULL!\n");
       exit(-5);
    }

    pthread_mutex_lock(&tp->mtx);    //锁住任务队列

    if(tp->waiting>=tp->max_queue){  //任务队列已满

       printf("queue is full!\n");
       exit(-6);
    }

    task->next=NULL;
    *tp->queue.last=task;       //尾指针指向新进入的任务节点,尾指针等于 next 指针的地址,便于下一次插入时使用
    tp->queue.last=&task->next;
    ++tp->waiting;

    if(pthread_cond_signal(&tp->cond)){  //发送一个信号,唤醒一个线程

       printf("signal faile!\n");
       pthread_mutex_unlock(&tp->mtx);
       exit(-7);
    }

    pthread_mutex_unlock(&tp->mtx);    //解锁

    return 1;
}


void thread_pool_cycle(thread_pool_t* tp){   //线程池执行函数,不断地从任务队列中取得任务并执行

     printf("--------thread_pool_cycle----------\n");
     if(!tp){

        printf("tp is a NULL");
        exit(-8);
     }

     while(1){

          pthread_mutex_lock(&tp->mtx);   //锁住

          //thread_task_t* task=tp->queue.first;
          while(!tp->queue.first)  pthread_cond_wait(&tp->cond,&tp->mtx);   //任务队列无任务,挂起线程

          thread_task_t* task=tp->queue.first;  //取出任务节点
          tp->queue.first=task->next;

          if(tp->queue.first==NULL)  tp->queue.last=&tp->queue.first;  //当前任务队列为空,使任务队列恢复到初始状态

          --tp->waiting;
          pthread_mutex_unlock(&tp->mtx);   //解锁

          //--tp->waiting;

          task->handler(task->ctx);     //执行任务

          free(task);   //释放已经处理的任务节点
     }

}


thread_task_t*  thread_task_alloc(int size){   //给任务节点分配内存

          printf("--------thread_task_alloc--------\n");

          thread_task_t* task=NULL;
          task=calloc(1,sizeof(thread_task_t)+size);  //分配任务节点内存的同时分配任务节点执行函数时所需参数的空间
          if(task==NULL){

             printf("task alloc faile!\n");
             exit(-9);
          }

          task->ctx=task+1;  //再把参数首地址赋给 ctx,这代码写的太厉害了,不愧是 Nginx

          return task;
}


static void* thread_pool_exit_handler(){

       printf("啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊\n");

       pthread_exit(0);

       return;
}


void thread_pool_destroy(thread_pool_t* tp){

     printf("---------thread_pool_destory---------\n");

     if(tp==NULL){

        printf("tp is NULL!\n");
        exit(-10);
     }

     thread_task_t* task=thread_task_alloc(0);
     task->handler=thread_pool_exit_handler;

     int i;
     for(i=0;i<tp->threads;++i)  thread_task_post(tp,task);  //在任务队列中加入等于线程数

     pthread_mutex_destroy(&tp->mtx);
     pthread_cond_destroy(&tp->cond);

     return;
}

6. Nginx 完整代码

Nginx 线程池