Nginx 線程池 (ngx_thread_pool) 是其異步 I/O 能力的核心支撐,將阻塞操作(文件讀寫、DNS 解析、加密計算)卸載到工作線程,保持事件循環非阻塞。本文拆解其關鍵數據結構、任務調度流程、鎖與條件變量協作、動態線程管理,並提供精簡可用的教學級實現 zoey_threadpool。
// 1. 任務節點
typedef void (*ngx_thread_task_handler_t)(void *ctx, ngx_log_t *log);
typedef struct {
ngx_thread_task_handler_t handler; // 執行函數
void *ctx; // 上下文參數
ngx_thread_task_t *next; // 鏈表指針
} ngx_thread_task_t;
// 2. 任務隊列(無鎖友好設計:head + tail 指針指向指針)
typedef struct {
ngx_thread_task_t *first; // 隊列頭
ngx_thread_task_t **last; // 指向尾節點 next 域的指針,O(1) 入隊
ngx_atomic_t count; // 任務計數
} ngx_thread_queue_t;
// 3. 線程池
typedef struct {
ngx_thread_queue_t queue; // 任務隊列
ngx_int_t threads; // 線程數
ngx_int_t max_queue; // 最大隊列長度
pthread_mutex_t mutex; // 互斥鎖
pthread_cond_t cond; // 條件變量
ngx_log_t *log; // 日誌
// ... 其他字段
} ngx_thread_pool_t;
ngx_thread_pool_initngx_thread_pool_t。queue.last = &queue.first)。pthread_create(&tid, &attr, ngx_thread_pool_cycle, pool)。ngx_thread_task_postngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task) {
ngx_mutex_lock(&tp->mutex);
if (tp->queue.count >= tp->max_queue) { // 隊列滿
ngx_mutex_unlock(&tp->mutex);
return NGX_ERROR;
}
*tp->queue.last = task; // O(1) 尾插:tail 指向最後節點的 next
tp->queue.last = &task->next; // 更新 tail 指向新節點的 next
tp->queue.count++;
ngx_cond_signal(&tp->cond); // 喚醒一個等待線程
ngx_mutex_unlock(&tp->mutex);
return NGX_OK;
}
精妙點:last 是「指向尾節點 next 域的指針」,入隊無需遍歷,無需雙向鏈表,極簡高效。
ngx_thread_pool_cyclevoid *ngx_thread_pool_cycle(void *data) {
ngx_thread_pool_t *tp = data;
ngx_thread_task_t *task;
for (;;) {
ngx_mutex_lock(&tp->mutex);
// 隊列空則等待
while (tp->queue.first == NULL) {
ngx_cond_wait(&tp->cond, &tp->mutex); // 原子釋放鎖並休眠
}
// 取出頭任務
task = tp->queue.first;
tp->queue.first = task->next;
if (tp->queue.first == NULL) {
tp->queue.last = &tp->queue.first; // 隊列空,重置 tail
}
tp->queue.count--;
ngx_mutex_unlock(&tp->mutex);
// 執行任務(無鎖,最大化並行)
task->handler(task->ctx, tp->log);
}
}
ngx_thread_pool_destroypthread_exit。pthread_join 等待所有線程退出,銷毀鎖、條件變量、釋放內存。去除 Nginx 專用日誌、內存池、錯誤碼,保留核心邏輯,適合嵌入式/通用 C 項目移植。
// zoey_threadpool.h #ifndef ZOEY_THREADPOOL_H #define ZOEY_THREADPOOL_H #include#include typedef void (*zoey_task_fn)(void *arg); typedef struct zoey_task { zoey_task_fn fn; void *arg; struct zoey_task *next; } zoey_task_t; typedef struct { zoey_task_t *head; zoey_task_t **tail; uint32_t max_tasks; uint32_t cur_tasks; } zoey_task_queue_t; typedef struct { pthread_mutex_t mutex; pthread_cond_t cond; zoey_task_queue_t queue; uint32_t thread_cnt; size_t stack_size; volatile uint32_t shutdown; // 原子關閉標誌 } zoey_threadpool_t; typedef struct { uint32_t thread_cnt; size_t stack_size; uint32_t max_tasks; } zoey_pool_conf_t; zoey_threadpool_t *zoey_pool_create(const zoey_pool_conf_t *conf); int zoey_pool_submit(zoey_threadpool_t *pool, zoey_task_fn fn, void *arg); void zoey_pool_destroy(zoey_threadpool_t *pool); int zoey_pool_add_thread(zoey_threadpool_t *pool); void zoey_pool_set_max_tasks(zoey_threadpool_t *pool, uint32_t max); #endif
// zoey_threadpool.c 關鍵片段
zoey_threadpool_t *zoey_pool_create(const zoey_pool_conf_t *conf) {
zoey_threadpool_t *pool = calloc(1, sizeof(*pool));
pool->queue.tail = &pool->queue.head;
pool->queue.max_tasks = conf->max_tasks;
pthread_mutex_init(&pool->mutex, NULL);
pthread_cond_init(&pool->cond, NULL);
// 創建線程...
for (uint32_t i = 0; i < conf->thread_cnt; ++i) {
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, conf->stack_size);
pthread_create(&tid, &attr, worker_loop, pool);
pthread_attr_destroy(&attr);
}
return pool;
}
int zoey_pool_submit(zoey_threadpool_t *pool, zoey_task_fn fn, void *arg) {
zoey_task_t *task = malloc(sizeof(*task));
task->fn = fn; task->arg = arg; task->next = NULL;
pthread_mutex_lock(&pool->mutex);
if (pool->queue.cur_tasks >= pool->queue.max_tasks) {
pthread_mutex_unlock(&pool->mutex);
free(task); return -1;
}
*pool->queue.tail = task;
pool->queue.tail = &task->next;
pool->queue.cur_tasks++;
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mutex);
return 0;
}
static void *worker_loop(void *arg) {
zoey_threadpool_t *pool = arg;
for (;;) {
pthread_mutex_lock(&pool->mutex);
while (pool->queue.head == NULL && !pool->shutdown) {
pthread_cond_wait(&pool->cond, &pool->mutex);
}
if (pool->shutdown && pool->queue.head == NULL) {
pthread_mutex_unlock(&pool->mutex);
break;
}
zoey_task_t *task = pool->queue.head;
pool->queue.head = task->next;
if (pool->queue.head == NULL) pool->queue.tail = &pool->queue.head;
pool->queue.cur_tasks--;
pthread_mutex_unlock(&pool->mutex);
task->fn(task->arg);
free(task);
}
return NULL;
}
zoey_pool_add_thread 加鎖創建新線程,thread_cnt++。適用於任務積壓、延遲飆升場景。zoey_pool_set_max_tasks(pool, 0) 表示無上限(需防止 OOM)。thread_cnt--,需處理線程安全退出同步。| 優化點 | Nginx 做法 | 原理 |
|---|---|---|
| 隊列尾插 O(1) | tail 指向指針 | 避免遍歷、無雙向鏈表開銷 |
| 鎖粒度 | 單互斥鎖保護整隊列 | 任務提交/取出極快,鎖爭用極低;高爭用可分段鎖 |
| 條件變量 | pthread_cond_signal 喚醒一個 | 避免驚群效應 |
| 線程棧大小 | 可配置,默認 256KB | 節省內存,適配嵌入式 |
| 任務上下文 | void *ctx 單參數 | 靈活傳遞任意數據,調用者管理生命周期 |