nginx线程池浅析

nginx线程池

源代码在ngx_thread_pool.c中

nginx线程池简化流程

A线程准备任务—>post任务—>线程池唤醒一个线程处理–>通过回调函数通知A线程处理完毕

数据结构分析

任务

struct ngx_thread_task_s {
    ngx_thread_task_t   *next;		//指向下一个任务
    ngx_uint_t           id;		//任务ID
    void                *ctx;		//任务的上下文
    void               (*handler)(void *data, ngx_log_t *log);		//处理任务的函数句柄
    ngx_event_t          event;			//跟任务关联的事件对象(当线程池处理成任务之后将会由主线程调用event对象的handler回调函数)
};

队列

typedef struct {
    ngx_thread_task_t        *first;
    ngx_thread_task_t       **last;
} ngx_thread_pool_queue_t;

线程池

struct ngx_thread_pool_s {
    ngx_thread_mutex_t        mtx;		//互斥锁,用于锁定任务队列,避免竞争状态。
    ngx_thread_pool_queue_t   queue;	//任务队列
    ngx_int_t                 waiting;	//有多少个任务正在等待处理
    ngx_thread_cond_t         cond;		//用于通知线程池有任务需要处理

    ngx_log_t                *log;		

    ngx_str_t                 name;		//线程池名称
    ngx_uint_t                threads;	//线程池由多少个线程组成(线程数)
    ngx_int_t                 max_queue;	//线程池最大能处理的任务数

    u_char                   *file;		//配置文件名
    ngx_uint_t                line;		//thread_pool配置在配置文件中的行号
};

代码分析

入口函数ngx_thread_pool_init_worker

此函数会调用线程池初始化函数

static ngx_int_t
ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
{
    ngx_uint_t                i;
    ngx_thread_pool_t       **tpp;
    ngx_thread_pool_conf_t   *tcf;

	//如果不是worker或者只有一个worker就不起用线程池
    if (ngx_process != NGX_PROCESS_WORKER
        && ngx_process != NGX_PROCESS_SINGLE)
    {
        return NGX_OK;
    }

    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                  ngx_thread_pool_module);

    if (tcf == NULL) {
        return NGX_OK;
    }

    ngx_thread_pool_queue_init(&ngx_thread_pool_done);		//初始化任务完成队列

    tpp = tcf->pools.elts;

    for (i = 0; i < tcf->pools.nelts; i++) {
        if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) {
            return NGX_ERROR;
        }
    }

    return NGX_OK;
}

线程池初始化ngx_thread_pool_init

static ngx_int_t
ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)
{
    int             err;
    pthread_t       tid;
    ngx_uint_t      n;
    pthread_attr_t  attr;

    if (ngx_notify == NULL) {
        ngx_log_error(NGX_LOG_ALERT, log, 0,
               "the configured event method cannot be used with thread pools");
        return NGX_ERROR;
    }
	
	/*
		#define ngx_thread_pool_queue_init(q)                                         \
		    (q)->first = NULL;                                                        \
		    (q)->last = &(q)->first
		
		first指针赋值NULL,**last执行first的地址
	*/
    ngx_thread_pool_queue_init(&tp->queue);		//初始化线程池队列

    if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) {
        return NGX_ERROR;
    }

    if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) {
        (void) ngx_thread_mutex_destroy(&tp->mtx, log);
        return NGX_ERROR;
    }

    tp->log = log;

    err = pthread_attr_init(&attr);
    if (err) {
        ngx_log_error(NGX_LOG_ALERT, log, err,
                      "pthread_attr_init() failed");
        return NGX_ERROR;
    }

    err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);		//是指线程的属性为分离
    if (err) {
        ngx_log_error(NGX_LOG_ALERT, log, err,
                      "pthread_attr_setdetachstate() failed");
        return NGX_ERROR;
    }

#if 0
    err = pthread_attr_setstacksize(&attr, PTHREAD_STACK_MIN);
    if (err) {
        ngx_log_error(NGX_LOG_ALERT, log, err,
                      "pthread_attr_setstacksize() failed");
        return NGX_ERROR;
    }
#endif

    for (n = 0; n < tp->threads; n++) {
        err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);
        if (err) {
            ngx_log_error(NGX_LOG_ALERT, log, err,
                          "pthread_create() failed");
            return NGX_ERROR;
        }
    }

    (void) pthread_attr_destroy(&attr);

    return NGX_OK;
}

线程运行函数

static void *
ngx_thread_pool_cycle(void *data)
{
    ngx_thread_pool_t *tp = data;

    int                 err;
    sigset_t            set;
    ngx_thread_task_t  *task;

#if 0
    ngx_time_update();
#endif

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, tp->log, 0,
                   "thread in pool \"%V\" started", &tp->name);

    sigfillset(&set);

    sigdelset(&set, SIGILL);
    sigdelset(&set, SIGFPE);
    sigdelset(&set, SIGSEGV);
    sigdelset(&set, SIGBUS);

    err = pthread_sigmask(SIG_BLOCK, &set, NULL);
    if (err) {
        ngx_log_error(NGX_LOG_ALERT, tp->log, err, "pthread_sigmask() failed");
        return NULL;
    }

    for ( ;; ) {
    	//获取任务前先锁定
        if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
            return NULL;
        }

        /* the number may become negative */
        tp->waiting--;
		
		//判断任务队列是否为空,假如为空则条件等待
        while (tp->queue.first == NULL) {
            if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log)
                != NGX_OK)
            {
                (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
                return NULL;
            }
        }
	
		//获取任务
        task = tp->queue.first;
        tp->queue.first = task->next;

        if (tp->queue.first == NULL) {
            tp->queue.last = &tp->queue.first;
        }

        if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) {
            return NULL;
        }

#if 0
        ngx_time_update();
#endif

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
                       "run task #%ui in thread pool \"%V\"",
                       task->id, &tp->name);

		//处理任务
        task->handler(task->ctx, tp->log);

        ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
                       "complete task #%ui in thread pool \"%V\"",
                       task->id, &tp->name);

        task->next = NULL;

		//通过自旋锁锁定,自旋锁会一直循环,不会让出cpu,适用于执行时间消耗小,比切换线程消耗的时间少
        ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);

		//把处理完的任务放入完成队列
        *ngx_thread_pool_done.last = task;
        ngx_thread_pool_done.last = &task->next;

        ngx_memory_barrier();		//防止编译器优化,保证解锁操作是在上述语句执行完毕后再去执行的

        ngx_unlock(&ngx_thread_pool_done_lock);

        (void) ngx_notify(ngx_thread_pool_handler);		//通知主线程
    }
}

主线程post任务

ngx_int_t
ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
	//判断任务是否已经添加过
    if (task->event.active) {
        ngx_log_error(NGX_LOG_ALERT, tp->log, 0,
                      "task #%ui already active", task->id);
        return NGX_ERROR;
    }

    if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
        return NGX_ERROR;
    }

	//待处理任务数达到线程池所能处理的最大数量,则报错
    if (tp->waiting >= tp->max_queue) {
        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);

        ngx_log_error(NGX_LOG_ERR, tp->log, 0,
                      "thread pool \"%V\" queue overflow: %i tasks waiting",
                      &tp->name, tp->waiting);
        return NGX_ERROR;
    }

	//置位任务是否已经添加到任务队列
    task->event.active = 1;
	//ngx_thread_pool_task_id是一个全局变量,每添加一个任务则自增
    task->id = ngx_thread_pool_task_id++;
    task->next = NULL;

    if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {
        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
        return NGX_ERROR;
    }

	//插入队列的一个小技巧,*last指向task,task赋值为task->next的地址,待下次添加时,把newtask赋值给*last,即task->next=newtask
    *tp->queue.last = task;
    tp->queue.last = &task->next;
	//待处理任务数自增
    tp->waiting++;

    (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);

    ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
                   "task #%ui added to thread pool \"%V\"",
                   task->id, &tp->name);

    return NGX_OK;
}

通知主线程函数ngx_thread_pool_handler

static void
ngx_thread_pool_handler(ngx_event_t *ev)
{
    ngx_event_t        *event;
    ngx_thread_task_t  *task;

    ngx_log_debug0(NGX_LOG_DEBUG_CORE, ev->log, 0, "thread pool handler");

	//函数:基于原子操作的自旋锁方法ngx_spinlock的实现
	//参数解释:
	//lock:原子变量表达的锁
	//value:标志位,锁是否被某一进程占用
	//spin:在多处理器系统内,当ngx_spinlock方法没有拿到锁时,当前进程在内核的一次调度中该方法等待其他处理器释放锁的时间
    ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);

	//取出完成队列中所有的任务
    task = ngx_thread_pool_done.first;
    ngx_thread_pool_done.first = NULL;
    ngx_thread_pool_done.last = &ngx_thread_pool_done.first;

    ngx_memory_barrier();

    ngx_unlock(&ngx_thread_pool_done_lock);

    while (task) {
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
                       "run completion handler for task #%ui", task->id);

		//获取任务中的完成后事件
        event = &task->event;
        task = task->next;
		//标志事件已完成,并且未处于任务队列中标志
        event->complete = 1;
        event->active = 0;
		//执行事件
        event->handler(event);
    }
}

线程池结束函数

static void
ngx_thread_pool_exit_worker(ngx_cycle_t *cycle)
{
    ngx_uint_t                i;
    ngx_thread_pool_t       **tpp;
    ngx_thread_pool_conf_t   *tcf;

    if (ngx_process != NGX_PROCESS_WORKER
        && ngx_process != NGX_PROCESS_SINGLE)
    {
        return;
    }

    tcf = (ngx_thread_pool_conf_t *) ngx_get_conf(cycle->conf_ctx,
                                                  ngx_thread_pool_module);

    if (tcf == NULL) {
        return;
    }

    tpp = tcf->pools.elts;		//获取线程池的数组

    for (i = 0; i < tcf->pools.nelts; i++) {
        ngx_thread_pool_destroy(tpp[i]);	//销毁各线程池
    }
}

销毁线程池

static void
ngx_thread_pool_destroy(ngx_thread_pool_t *tp)
{
    ngx_uint_t           n;
    ngx_thread_task_t    task;
    volatile ngx_uint_t  lock;

    ngx_memzero(&task, sizeof(ngx_thread_task_t));

	//新建销毁线程池任务
    task.handler = ngx_thread_pool_exit_handler;
    task.ctx = (void *) &lock;

	//线程池中有多少个线程就发起多少个销毁消除池任务
    for (n = 0; n < tp->threads; n++) {
        lock = 1;
		//添加销毁任务至任务队列
        if (ngx_thread_task_post(tp, &task) != NGX_OK) {
            return;
        }
		//等到任务被执行完后在继续执行,否则让出cpu
        while (lock) {
            ngx_sched_yield();
        }

        task.event.active = 0;
    }

	//释放条件变量
    (void) ngx_thread_cond_destroy(&tp->cond, tp->log);
	//释放互斥锁
    (void) ngx_thread_mutex_destroy(&tp->mtx, tp->log);
}

thread_pool线程池的使用示例

nginx中的线程池主要是用于操作文件的IO操作。所以,在nginx中自带的模块ngx_http_file_cache.c文件中看到了线程池的使用。

/*********************** nginx/src/os/unix/ngx_files.c  **********************/
//file_cache模块的处理函数(涉及到了线程池)
static ssize_t  ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
{
    .......
#if (NGX_THREADS)
  
    if (clcf->aio == NGX_HTTP_AIO_THREADS) {
        c->file.thread_task = c->thread_task;
        //这里注册的函数在下面语句中的ngx_thread_read函数中被调用
        c->file.thread_handler = ngx_http_cache_thread_handler;
        c->file.thread_ctx = r;
        //根据任务的属性,选择正确的线程池,并初始化task结构体中的各个成员       
        n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
  
        c->thread_task = c->file.thread_task;
        c->reading = (n == NGX_AGAIN);
  
        return n;
    }
#endif
  
    return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
}
  
  
//task任务的处理函数
static ngx_int_t  ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
    .......
    tp = clcf->thread_pool;
    .......
     
    task->event.data = r;
    //注册thread_event_handler函数,该函数在处理pool_done队列中event事件时被调用
    task->event.handler = ngx_http_cache_thread_event_handler;
  
    //将任务放到线程池的任务队列中
    if (ngx_thread_task_post(tp, task) != NGX_OK) {
        return NGX_ERROR;
    }
    ......
}
  
/*********************** nginx/src/core/ngx_thread_pool.c  **********************/
//添加任务到队列中
ngx_int_t  ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
    //如果当前的任务正在处理就退出
    if (task->event.active) {
        ngx_log_error(NGX_LOG_ALERT, tp->log, 0,
                      "task #%ui already active", task->id);
        return NGX_ERROR;
    }
  
    if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
        return NGX_ERROR;
    }
     
    //判断当前线程池等待的任务数量与最大队列长度的关系
    if (tp->waiting >= tp->max_queue) {
        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
  
        ngx_log_error(NGX_LOG_ERR, tp->log, 0,
                      "thread pool \"%V\" queue overflow: %i tasks waiting",
                      &tp->name, tp->waiting);
        return NGX_ERROR;
    }
    //激活任务
    task->event.active = 1;
  
    task->id = ngx_thread_pool_task_id++;
    task->next = NULL;
      
    //通知阻塞的线程有新事件加入,可以解除阻塞
    if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {
        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
        return NGX_ERROR;
    }
  
    *tp->queue.last = task;
    tp->queue.last = &task->next;
  
    tp->waiting++;
  
    (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
  
    ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
                   "task #%ui added to thread pool \"%V\"",
                   task->id, &tp->name);
  
    return NGX_OK;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值