线程池的作用
线程池最主要的作用是对于任务处理的异步解耦。
比较耗时的任务,比如日志、数据库操作,可以扔到线程池去处理,不影响主线程的性能。
关于CPU和线程的亲缘性绑定:
主要注重CPU处理能力的话,可以进行CPU的亲缘性绑定;重在异步解耦,没必要将线程和CPU绑定。
线程池的原理
线程池中的角色
线程池中包含3种角色:
- worker,即工作线程,一个worker对应一个线程。包含线程id和线程结束标志。
- job,即任务,包含任务执行方法和参数。
- thread_pool, 对worker和job进行管理,分别包含一个worker队列和一个job队列。
线程池与连接池不一样,线程之间是一种争夺式的关系。线程的核心工作就是取任务,执行。
外界只需要往任务队列里面抛任务就可以了。
线程入口函数,里面有一个while的循环,不断从任务队列里面取任务,调用任务的回调函数去执行。
任务队列是线程池里面所有线程都可以访问的临界资源,A线程能从队列中取任务,B线程也可以取任务。
typedef struct _worker {
pthread_t id;
int terminate;
struct _thread_pool *pool;
struct _worker *prev;
struct _worker *next;
} worker_t;
typedef struct _job {
void (*job_func)(void *arg);
void *user_data;
struct _job *prev;
struct _job *next;
} job_t;
typedef struct _thread_pool {
worker_t *workers;
job_t *jobs;
pthread_cond_t cond;
pthread_mutex_t mtx;
} thread_pool_t;
线程池接口
必要的3个接口:
- thread_pool_create(), 创建线程池
- thread_pool_destroy(), 销毁线程池
- thread_pool_push_job(), 往线程池里面放任务。
其他的接口,比如获取任务数量,空闲线程数量,可以作为扩展,用来动态的增加/减少线程数量。
int thread_pool_create(thread_pool_t *pool, int thread_num);
int thread_pool_destroy(thread_pool_t *pool);
int thread_pool_push_job(thread_pool_t *pool, job_t *job)
手写一个线程池
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
if (list != NULL) list->prev = item; \
list = item; \
} while (0)
#define LL_REMOVE(item, list) do { \
if (item->prev != NULL) item->prev->next = item->next; \
if (item->next != NULL) item->next->prev = item->prev; \
if (item == list) list = item->next; \
item->prev = item->next = NULL; \
} while (0)
typedef struct _worker {
pthread_t id;
int terminate;
struct _thread_pool *pool;
struct _worker *prev;
struct _worker *next;
} worker_t;
typedef struct _job {
void (*job_func)(void *arg);
void *user_data;
struct _job *prev;
struct _job *next;
} job_t;
typedef struct _thread_pool {
worker_t *workers;
job_t *jobs;
pthread_cond_t cond;
pthread_mutex_t mtx;
} thread_pool_t;
void * thread_callback(void *arg) {
worker_t *worker = (worker_t *)arg;
while (1) {
pthread_mutex_lock(&worker->pool->mtx);
while (worker->pool->jobs == NULL) {
if (worker->terminate) break;
pthread_cond_wait(&worker->pool->cond, &worker->pool->mtx);
}
if (worker->terminate) {
pthread_mutex_unlock(&worker->pool->mtx);
break;
}
job_t *job = worker->pool->jobs;
LL_REMOVE(job, worker->pool->jobs);
pthread_mutex_unlock(&worker->pool->mtx);
if (job == NULL) continue;
job->job_func(job);
}
free(worker);
}
int thread_pool_create(thread_pool_t *pool, int thread_num) {
if (pool == NULL) return -1;
if (thread_num < 1) thread_num = 1;
memset(pool, 0, sizeof(thread_pool_t));
pool->cond = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
pool->mtx = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
int idx = 0;
for (idx = 0; idx < thread_num; idx++) {
worker_t *worker = (worker_t *)malloc(sizeof(worker_t));
if (worker == NULL) {
perror("malloc");
return idx;
}
memset(worker, 0, sizeof(worker_t));
worker->pool = pool;
int ret = pthread_create(&worker->id, NULL, thread_callback, worker);
if (ret) {
perror("pthread_create");
free(worker);
return idx;
}
}
return idx;
}
int thread_pool_destroy(thread_pool_t *pool) {
if (pool == NULL) return -1;
worker_t *worker = NULL;
for (worker = pool->workers; worker != NULL; worker = worker->next) {
worker->terminate = 1;
}
pthread_mutex_lock(&pool->mtx);
pthread_cond_broadcast(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
return 0;
}
int thread_pool_push_job(thread_pool_t *pool, job_t *job) {
if (pool == NULL || job == NULL) return -1;
pthread_mutex_lock(&pool->mtx);
LL_ADD(job, pool->jobs);
pthread_cond_signal(&pool->cond);
pthread_mutex_unlock(&pool->mtx);
return 0;
}
#if 1
void count(void *arg) {
job_t *job = (job_t *)arg;
int idx = *(int *)job->user_data;
printf("idx = %d, thread_id = %lu\n", idx, pthread_self());
free(job->user_data);
free(job);
}
int main() {
thread_pool_t pool = {0};
const int thread_num = 20;
thread_pool_create(&pool, thread_num);
const int task_num = 1000;
int i = 0;
for (i = 0; i < task_num; i++) {
job_t *job = malloc(sizeof(job_t));
if (job == NULL) exit(1);
memset(job, 0, sizeof(job_t));
job->job_func = count;
job->user_data = malloc(sizeof(int));
if (job->user_data == NULL) exit(1);
*(int *)job->user_data = i;
thread_pool_push_job(&pool, job);
}
getchar();
thread_pool_destroy(&pool);
sleep(3);
}
#endif
nginx线程池
nginx里面的线程池跟我们上面的线程池代码类似,也是三个主要接口
static ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool);
static void ngx_thread_pool_destroy(ngx_thread_pool_t *tp);
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task);