【Linux网络编程】Nginx -- 线程池

【1】Nginx 线程池概述

Nginx 线程池相关代码路径如下,src\core\ngx_thread_pool.h/ngx_thread_pool.c

编译时使用如下选项可以启用线程池功能

--with-threads
--with-file-aio

启用线程池功能,让请求排队等待处理,并且可以充分利用 CPU 提高处理效率,开启线程池需要 AIO 的支持,启用异步文件 IO (AIO) 一般用于大文件传输的场景;

Nginx 线程池源码分析

线程池处理图示

nginx thread pool nginx thread pool 配置_线程池

【2】ngx_thread_pool_module -- Nginx 线程池模块定义

【2.1】ngx_thread_pool_module

该模块存储于全局变量 ngx_modules 中,ngx_modules 声明与定义如下

// 模块的名字数组,由 make 生成在 objs/ngx_modules.c
extern char          *ngx_module_names[];

// 文件路径,objs/ngx_modules.c
extern ngx_module_t  ngx_thread_pool_module;

ngx_module_t *ngx_modules[] = {
    ...

    &ngx_thread_pool_module,

    ...
}

char *ngx_module_names[] = {
    ...

    "ngx_thread_pool_module",

    ...
}

ngx_thread_pool_module 模块在 src\core\ngx_cycle.c 文件的 ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle) 函数中遍历获取;

ngx_module_t  ngx_thread_pool_module = {
    NGX_MODULE_V1,
    &ngx_thread_pool_module_ctx,           /* module context */
    ngx_thread_pool_commands,              /* module directives */
    NGX_CORE_MODULE,                       /* module type */
    NULL,                                  /* init master */
    NULL,                                  /* init module */

    // ngx_single_process_cycle/ngx_worker_process_cycle里调用
    // 进程开始时初始化,创建线程池
    ngx_thread_pool_init_worker,           /* init process */

    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */

    // 进程结束时被调用,清理线程池
    // 调用ngx_thread_pool_destroy逐个销毁线程池
    ngx_thread_pool_exit_worker,           /* exit process */

    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
  • ngx_thread_pool_init_worker 调用位置,关键代码如下
static void
ngx_worker_process_init(ngx_cycle_t *cycle, ngx_int_t worker)
{
	...
	
    // 调用所有模块的 init_process,模块进程初始化 hook
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->init_process) {
            if (cycle->modules[i]->init_process(cycle) == NGX_ERROR) {
                /* fatal */
                exit(2);
            }
        }
    }	
	
	...
}

ngx_thread_pool_init_worker 函数分析

// 进程开始时初始化,创建线程池
// 1. 获取线程池配置项
// 2. 初始化线程池的任务队列
// 3. 调用 ngx_thread_pool_init 函数初始化线程池
static ngx_int_t ngx_thread_pool_init_worker(ngx_cycle_t *cycle);
  • ngx_thread_pool_exit_worker 调用位置,关键代码如下
static void
ngx_worker_process_exit(ngx_cycle_t *cycle)
{
	...
	
    // 调用所有模块的 exit_process,进程结束 hook
    for (i = 0; cycle->modules[i]; i++) {
        if (cycle->modules[i]->exit_process) {
            cycle->modules[i]->exit_process(cycle);
        }
    }	
	
	...
}

ngx_thread_pool_exit_worker 函数分析

// 进程结束时被调用,清理线程池
// 1. 判断进程类型,确保进程是 worker/single 进程
// 2. 获取线程池模块的配置项
// 3. 调用 ngx_thread_pool_destroy 逐个销毁线程池
static void ngx_thread_pool_exit_worker(ngx_cycle_t *cycle);

【2.2】ngx_thread_pool_module_ctx

// 线程池模块属于core模块,只有一个指令,配置有名的线程池
static ngx_core_module_t  ngx_thread_pool_module_ctx = {
    ngx_string("thread_pool"),

    // 创建线程池模块的配置,里面是个数组,元素为ngx_thread_pool_t
    ngx_thread_pool_create_conf,

    // 检查配置的线程池,必须设置线程数量
    ngx_thread_pool_init_conf
};
  • ngx_thread_pool_create_conf 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
	
    ngx_core_module_t   *module;

    // 初始化 core 模块
    for (i = 0; cycle->modules[i]; i++) {
        // 检查 type,只处理 core 模块,数量很少
        if (cycle->modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        //获取 core 模块的函数表
        module = cycle->modules[i]->ctx;

    //*******************************************************
    // 5. 遍历所有 core 模块,并调用该模块的 create_conf() 函数
    //*******************************************************
        // 创建 core 模块的配置结构体
        // 有的 core 模块可能没有这个函数,所以做一个空指针检查
        if (module->create_conf) {
            rv = module->create_conf(cycle);
            if (rv == NULL) {
                ngx_destroy_pool(pool);
                return NULL;
            }
            // 存储到 cycle 的配置数组里,用的是 index,不是 ctx_index
            cycle->conf_ctx[cycle->modules[i]->index] = rv;
        }
    }
    
    ...
}

ngx_thread_pool_create_conf 函数分析

// 创建线程池模块的配置,其中包含一个数组,数组元素为 ngx_thread_pool_t
static void *ngx_thread_pool_create_conf(ngx_cycle_t *cycle);
  • ngx_thread_pool_init_conf 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
	
    ngx_core_module_t   *module;

    //*******************************************************
    // 7. 遍历所有 core 模块,并调用 core 模块的 init_conf() 函数
    //*******************************************************
    // 其他类型的模块都已经配置好了,最后对 core 模块配置初始化
    // 如果有的参数没有明确配置,这里就调用 init_conf 设置默认值
    for (i = 0; cycle->modules[i]; i++) {
        // 检查 type,只处理 core 模块,数量很少
        if (cycle->modules[i]->type != NGX_CORE_MODULE) {
            continue;
        }

        // 获取 core 模块的函数表
        module = cycle->modules[i]->ctx;

        // 调用 core 模块的初始化函数
        // 有的 core 模块可能没有这个函数,所以做一个空指针检查
        if (module->init_conf) {
            if (module->init_conf(cycle,
                                  cycle->conf_ctx[cycle->modules[i]->index])
                == NGX_CONF_ERROR)
            {
                environ = senv;
                ngx_destroy_cycle_pools(&conf);
                return NULL;
            }
        }
    }

    ...
}

ngx_thread_pool_init_conf 函数分析

// 检查线程池的配置参数,在未配置的情况下给出默认的配置参数
// threads : 32,max_queue : 65536
static char *ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf);

【2.3】ngx_thread_pool_commands

// 线程池模块属于core模块,只有一个指令,配置有名的线程池
// 解析thread_pool指令,设置线程数和队列数(默认65535)
static ngx_command_t  ngx_thread_pool_commands[] = {

    { ngx_string("thread_pool"),
      NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE23,
      ngx_thread_pool,
      0,
      0,
      NULL },

      ngx_null_command
};
  • ngx_thread_pool 调用位置,关键代码如下
ngx_cycle_t * ngx_init_cycle(ngx_cycle_t *old_cycle)
{
    ...
	
    ngx_conf_t           conf;
    ngx_core_module_t   *module;

    //*******************************************************
    // 6. 配置文件解析
    //*******************************************************
    // 递归执行解析动作,各个模块允许的指令配置参数
    // 先解析-g传递的命令行参数
    if (ngx_conf_param(&conf) != NGX_CONF_OK) {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }

    // 递归执行解析动作,各个模块允许的指令配置参数
    // 里面有http的post configration指针
    // 再解析配置文件
    if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {
        environ = senv;
        ngx_destroy_cycle_pools(&conf);
        return NULL;
    }

    ...
}

文件路径,src\core\ngx_conf_file.c
char *
ngx_conf_parse(ngx_conf_t *cf, ngx_str_t *filename)
{
	...
	
    rc = ngx_conf_handler(cf, rc);	
	
	...
}

文件路径,src\core\ngx_conf_file
static ngx_int_t
ngx_conf_handler(ngx_conf_t *cf, ngx_int_t last)
{
    ...

    // 遍历所有模块
    for (i = 0; cf->cycle->modules[i]; i++) {

        // 取模块的指令数组
        cmd = cf->cycle->modules[i]->commands;
			
			...
			
            // 在此处调用设置配置参数
            rv = cmd->set(cf, cmd, conf);	
			
			...
	...
}

ngx_thread_pool 函数分析

// 解析 thread_pool 指令,设置线程数和队列数(默认 65535)
// 保证配置中至少包含一个默认的配置项
static char *ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

【3】Nginx 线程池相关结构体

【3.1】ngx_thread_task_s/ngx_thread_task_t 结构体

// 线程池任务结构体
struct ngx_thread_task_s {
    // 链表指针,多个 task 形成一个链表
    ngx_thread_task_t   *next;

    // task 的 id,由全局计数器 ngx_thread_pool_task_id 生成
    // task->id = ngx_thread_pool_task_id++;
    // 在线程运行时用户的 handler 看不到
    ngx_uint_t           id;

    // 用户使用的数据,也就是 handler 的 data 参数
    // 用这个参数传递给线程要处理的各种数据
    // 比较灵活的方式是传递一个指针,而不是真正的数据结构内容
    // 由 ngx_thread_task_alloc 分配内存空间并赋值
    void                *ctx;

    // 由线程里的线程执行的函数
    // 执行用户定义的操作,通常是阻塞的
    // 参数 data 就是上面的 ctx
    // handler 不能直接看到 task,但可以在 ctx 里存储 task 指针
    void               (*handler)(void *data, ngx_log_t *log);

    // 任务关联的事件对象
    // event.active 表示任务是否已经放入任务队列
    // 这里的 event 并不关联任何 socket 读写或定时器对象
    // 仅用到它的 handler/data 成员,当线程完成任务时回调
    //
    // event->data 要存储足够的信息,才能够完成请求
    // 可以使用 r->ctx,里面存储请求 r、连接 c 等
    ngx_event_t          event;
};

【3.2】ngx_thread_pool_queue_t 结构体

// 线程池使用的任务队列
typedef struct {
    ngx_thread_task_t        *first;
    ngx_thread_task_t       **last;
} ngx_thread_pool_queue_t;

【3.3】ngx_thread_pool_s/ngx_thread_pool_t 结构体

// 描述一个线程池,与 thread_pool 指令对应
// 此结构体的实际定义在 c 文件里,外部不可见,深度定制则不方便
// 存储在 ngx_thread_pool_conf_t 里的数组里
// 核心成员是 queue,存储待处理的任务
struct ngx_thread_pool_s {
    // 互斥量
    // 锁定互斥量,防止多线程操作的竞态
    // 锁定操作 waiting/queue/ngx_thread_pool_task_id
    ngx_thread_mutex_t        mtx;

    // 线程池里的待处理任务队列
    // ngx_thread_task_post 把任务放入线程池
    // ngx_thread_pool_cycle 消费任务
    ngx_thread_pool_queue_t   queue;

    // 等待的任务数
    ngx_int_t                 waiting;

    // 文件 src\os\unix\ngx_thread.h 中
    // typedef pthread_cond_t  ngx_thread_cond_t;
    // 条件变量,用于等待任务队列 queue
    ngx_thread_cond_t         cond;

    // 日志对象,多线程操作也是安全的
    ngx_log_t                *log;

    // 线程池的名字
    ngx_str_t                 name;

    // 线程的数量,默认为 32 个线程
    ngx_uint_t                threads;

    // 任务等待队列,默认是 65535
    ngx_int_t                 max_queue;

    // 定义线程池的配置文件
    u_char                   *file;

    // 定义线程池指令的行号
    ngx_uint_t                line;
};

【3.4】ngx_thread_pool_conf_t 结构体

// 线程池模块的配置,其中是个数组,元素为 ngx_thread_pool_t/(ngx_thread_pool_s)
// 由 ngx_thread_pool_add 在解析指令的 ngx_thread_pool 函数时添加
// 在 ngx_thread_pool_create_conf 函数中创建
typedef struct {
    ngx_array_t               pools;
} ngx_thread_pool_conf_t;

【4】Nginx 线程池相关函数

【4.1】ngx_thread_pool_init 函数分析

// 使用 ngx_thread_pool_t 结构体初始化线程池
// 在 init_worker 时被调用
// 创建互斥量、条件变量,根据配置的线程数量,创建线程
// 线程的执行函数是 ngx_thread_pool_cycle,参数是线程池结构体
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;

    // 要求必须有事件通知函数 ngx_notify
    // 否则多线程无法工作
    // 调用系统函数 eventfd,创建一个可以用于通知的描述符
    //
    // ngx_notify :
    // 文件路径,src\event\ngx_event.h
    // #define ngx_notify           ngx_event_actions.notify
    // ngx_event_actions 为全局变量
    // 文件路径,src\event\ngx_event.h
    // extern ngx_event_actions_t   ngx_event_actions;
    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;
    }

    // 初始化线程池任务队列,first/last都是空
    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;
    }

    // 线程池使用的log由外部传入
    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;
    }

    // 线程 detach
    // 设置线程为分离状态,分离线程无须被其他线程等待,自己运行结束线程便终了,立马释放资源
    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++) {

        // 线程的执行函数是 ngx_thread_pool_cycle,参数是线程池结构体
        // 线程创建后立即 detach
        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;
}

【4.2】ngx_thread_pool_cycle 函数分析

// 线程池里每个线程执行的函数,无限循环
// 参数是线程池结构体
// 从待处理任务队列里获取任务,然后执行 task->handler(task->ctx)
// 处理完的任务加入完成队列,触发 ngx_thread_pool_handler 函数处理
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);

    // 原型
    // int pthread_sigmask (int how,const sigset_t *set,sigset_t *oset)
    // 参数
    //     SIG_BLOCK   : 结果集是当前集合参数集的并集;
    //     SIG_UNBLOCK : 结果集是当前集合参数集的差集;
    //     SIG_SETMASK : 结果集是由参数集指向的集;
    //
    // 只在主线程中处理信号
    err = pthread_sigmask(SIG_BLOCK, &set, NULL);
    if (err) {
        ngx_log_error(NGX_LOG_ALERT, tp->log, err, "pthread_sigmask() failed");
        return NULL;
    }

    // 无限循环
    // 从待处理任务队列里获取任务,然后执行 task->handler(task->ctx)
    for ( ;; ) {
        // 锁定互斥量,防止多线程操作的竞态
        if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
            return NULL;
        }

        /* the number may become negative */
        // 即将处理一个任务,计数器减 1
        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

        // 取出一个 task
        task = tp->queue.first;
        tp->queue.first = task->next;

        // 如果此时队列已经空,调整指针
        if (tp->queue.first == NULL) {
            tp->queue.last = &tp->queue.first;
        }

        // 操作完 waiting、queue 后解锁,其他线程可以获取 task 处理
        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);

        // 调用任务的 handler,传递 ctx,执行用户定义的操作,通常是阻塞的
        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;

        // 自旋锁保护完成队列
        ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);

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

        // 1.10 新增
        // 确保内存操作按照正确的顺序工作的非阻塞的同步
        // 迫使处理器来完成位于 barrier 前面的任何加载和存储操作
        // 才允许它执行位于 barrier 之后的加载和存储操作
        ngx_memory_barrier();

        // 自旋锁解锁
        ngx_unlock(&ngx_thread_pool_done_lock);

        // 重要,使用 event 模块的通知函数
        // 让主线程(nginx)的 epoll 触发事件,调用 ngx_thread_pool_handler
        // 分发处理线程完成的任务
        //
        // 调用系统函数 eventfd,创建一个可以用于通知的描述符,用于实现 notify
        (void) ngx_notify(ngx_thread_pool_handler);
    }   // 无限循环,回到开头再取下一个 task
}

【4.3】ngx_thread_pool_handler 函数分析

// 处理 ngx_thread_pool_done 队列中的已完成任务
// 调用 event->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(&ngx_thread_pool_done_lock, 1, 2048);

    // 取出队列里的 task,task->next 里有很多已经完成的任务
    task = ngx_thread_pool_done.first;

    // 把队列直接置空
    // 即 ngx_thread_pool_queue_init
    ngx_thread_pool_done.first = NULL;
    ngx_thread_pool_done.last = &ngx_thread_pool_done.first;

    // 1.10 新增
    // 确保内存操作按照正确的顺序工作的非阻塞的同步
    // 迫使处理器来完成位于 barrier 前面的任何加载和存储操作
    // 才允许它执行位于 barrier 之后的加载和存储操作
    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);

        // 取 task 里的事件对象
        event = &task->event;

        // task 指针移动到下一个节点
        task = task->next;

        // 线程异步事件已经完成
        event->complete = 1;

        // 事件已经处理完
        event->active = 0;

        // 调用 handler,即异步事件完成后的回调函数
        event->handler(event);
    }
}

【4.4】ngx_thread_pool_destroy 函数分析

// 销毁线程池
// 使用一个要求线程结束的 task,发给池里所有的线程
// 最后销毁条件变量和互斥量
static void
ngx_thread_pool_destroy(ngx_thread_pool_t *tp)
{
    ngx_uint_t           n;
    ngx_thread_task_t    task;

    // lock 是一个简单的标志量,作为任务的 ctx 传递
    volatile ngx_uint_t  lock;

    // 创建要求线程结束的 task
    ngx_memzero(&task, sizeof(ngx_thread_task_t));

    // 要求线程结束的任务,调用 pthread_exit
    task.handler = ngx_thread_pool_exit_handler;

    // lock 是一个简单的标志量,作为任务的 ctx 传递
    task.ctx = (void *) &lock;

    // 发送 tp->threads 个 task,逐个结束所有的线程
    for (n = 0; n < tp->threads; n++) {
        // 线程退出后将会被设置为 0
        lock = 1;

        // 把任务加入到线程池的队列
        if (ngx_thread_task_post(tp, &task) != NGX_OK) {
            return;
        }

        // 等待 task 被某个线程处理,从而结束一个线程
        while (lock) {
            // ngx_process.h:#define ngx_sched_yield()  sched_yield()
            // 避免占用 cpu,让出主线程执行权,其他线程有机会执行
            ngx_sched_yield();
        }

        // event.active 表示任务是否已经放入任务队列
        task.event.active = 0;
    }

    // 销毁条件变量
    (void) ngx_thread_cond_destroy(&tp->cond, tp->log);

    // 销毁互斥量
    (void) ngx_thread_mutex_destroy(&tp->mtx, tp->log);
}

【4.5】ngx_thread_pool_exit_handler 函数分析

// 要求线程结束的任务,调用 pthread_exit
// 把任务需要处理的 data 置为 0,表示线程结束
static void
ngx_thread_pool_exit_handler(void *data, ngx_log_t *log)
{
    ngx_uint_t *lock = data;

    *lock = 0;

    pthread_exit(0);
}

【4.6】ngx_thread_task_post 函数分析

// 把任务放入线程池,由线程执行
// 锁定互斥量,防止多线程操作的竞态
// 如果等待处理的任务数大于设置的最大队列数,那么添加任务失败
// 操作完 waiting、queue、ngx_thread_pool_task_id 后解锁
ngx_int_t
ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
    // event.active 表示任务是否已经放入任务队列
    // 如果 event.active==1 则 ngx_thread_task_post 失败
    // 避免任务被重复加入到任务队列中
    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;
    }

    // event.active 表示任务是否已经放入任务队列
    // 如果 event.active == 1 则任务已经添加到任务队列中
    task->event.active = 1;

    // task id 增加
    // 全局计数器, 生成 task 的 id
    task->id = ngx_thread_pool_task_id++;
    task->next = NULL;

    // 条件变量,发送信号
    // 在 ngx_thread_pool_cycle 里解除对队列的等待
    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++;

    // 操作完waiting、queue、ngx_thread_pool_task_id后解锁
    (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;
}

【4.7】ngx_thread_task_alloc 函数分析

// 创建一个线程任务结构体
// 参数 size 是用户数据 ctx 的大小,位于 task 之后
// 因为 C 的内存布局是平坦的,所以使用这种 hack 的方法来扩展 task 结构体
ngx_thread_task_t *
ngx_thread_task_alloc(ngx_pool_t *pool, size_t size)
{
    ngx_thread_task_t  *task;

    // 注意,多分配了size个字节,即用户要求的ctx大小
    task = ngx_pcalloc(pool, sizeof(ngx_thread_task_t) + size);
    if (task == NULL) {
        return NULL;
    }

    // 设置 task 的 ctx 指针,即在 task 地址之后的位置
    task->ctx = task + 1;

    return task;
}

【5】Nginx 使用实例简析

文件路径,src\http\ngx_http_file_cache.c
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_http_cache_thread_handler 中将线程池任务放入线程池的任务队列中
            c->file.thread_handler = ngx_http_cache_thread_handler;
            c->file.thread_ctx = r;

            // 此处使用线程池的方式异步读取文件数据
            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

    ...
}

文件路径,src\http\ngx_http_file_cache.c
static ngx_int_t
ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
    ...

    ngx_thread_pool_t         *tp;

    ...

    // 设置线程池任务并加入线程池任务队列中
    task->event.data = r;
    // 事件回调函数,当线程池任务处理完毕回调 ngx_http_cache_thread_event_handler 函数
    // 在 ngx_http_cache_thread_event_handler 中发送请求
    task->event.handler = ngx_http_cache_thread_event_handler;

    // 将线程池任务添加到线程池的任务队列中
    if (ngx_thread_task_post(tp, task) != NGX_OK) {
        return NGX_ERROR;
    }

    r->main->blocked++;
    r->aio = 1;    

    ...
}

文件路径,src\os\unix\ngx_files.c
ssize_t
ngx_thread_read(ngx_file_t *file, u_char *buf, size_t size, off_t offset,
    ngx_pool_t *pool)
{
    ...

    // 获取或新建线程池任务
    task = file->thread_task;

    // 如果没有task则创建
    if (task == NULL) {
        task = ngx_thread_task_alloc(pool, sizeof(ngx_thread_file_ctx_t));
        if (task == NULL) {
            return NGX_ERROR;
        }

        file->thread_task = task;
    }

    ...

    // 指定线程池任务的处理句柄
    //
    // ngx_thread_read_handler 函数中
    //      调用 pread 函数以原子定位读取数据
    task->handler = ngx_thread_read_handler;

    // 投递到线程池里执行
    // 任务完成后回调 event 里的 handler
    // 
    // 本例中 file->thread_handler 为 ngx_http_cache_thread_handler
    if (file->thread_handler(task, file) != NGX_OK) {
        return NGX_ERROR;
    }
    ...
}

参考致谢
本博客为博主的学习实践总结,并参考了众多博主的博文,在此表示感谢,博主若有不足之处,请批评指正。

【1】深入理解 Nginx 模块开发与架构解析

【2】理解 Nginx 源码

【3】NGINX引入线程池 性能提升9倍

【4】Nginx 的线程池与性能剖析