1.何为线程池。
把线程池想象成食堂打饭阿姨,每一份饭对应一个线程,线程池的作用便是像打饭阿姨一样在没人的时候等待,一旦有人刷卡(任务到来),阿姨便分发餐盘打菜(分配线程),吃完后再回收餐盘(回收线程)继续等待。当然,阿姨一怒之下也可以把餐盘摔了(结束线程)。同时可能有几个窗口(临界资源)进行打饭,所以不可避免的要加锁。
2.线程池的作用。
个人理解,线程池起到一个缓冲区的作用,等需要的时候从池子里拿,不要再放回,避免线程创建销毁的过渡开销,同时对线程起到一个管理作用。以及很重要的异步解耦(异步io)的作用。
3.代码
整个线程池大致分为这几个部分。
线程池创建销毁,添加任务,线程回调函数组成。而线程池的每个线程,和任务采用双向链表的方式存储。
#include <iostream>
#include <cstring>
#include <pthread.h>
template<class T>
void LL_ADD(T &item, T &list){ // head insert
item->prev = NULL;
item->next = list;
if (list!=NULL)
list->prev = item;
list = item;
}
template<class T>
void LL_REMOVE(T &item, T &list){
if (item->prev != NULL)
item->prev->next = item->next;
if (item->next != NULL)
item->next->prev = item->prev;
if (list == item)
list = item->next;
item->prev = item->next = NULL;
}
struct N_WORKER{ //线程双链表
pthread_t thread;
struct N_MANAGER *pool;
int terminate; //线程终止标致位
struct N_WORKER *prev;
struct N_WORKER *next;
};
struct N_JOB{ //任务双链表
void (*func)(int data, pthread_t threadId); //每个任务对应的任务函数 可自己修改
int user_data; //任务数据
struct N_JOB *prev;
struct N_JOB *next;
};
struct N_MANAGER{ //管理
struct N_WORKER *workers;
struct N_JOB *jobs;
pthread_cond_t jobs_cond; //条件变量 线程等待某个条件的满足否则挂起
pthread_mutex_t jobs_mutex; //锁
};
typedef struct N_MANAGER nThreadPool;
static void *nThreadCallBack(void *arg){ //本文件有效
struct N_WORKER *worker = (struct N_WORKER*)arg;
while (1){
pthread_mutex_lock(&worker->pool->jobs_mutex); //加锁
while(worker->pool->jobs == NULL){ //无任务
if(worker->terminate) break; //线程是否要求终止
pthread_cond_wait(&worker->pool->jobs_cond, &worker->pool->jobs_mutex); //函数里解锁,函数结束加锁,执行代码不在锁内 有风险
}
if(worker->terminate){
pthread_mutex_unlock(&worker->pool->jobs_mutex); //解锁
break;
}
struct N_JOB *job = worker->pool->jobs;
LL_REMOVE(job, worker->pool->jobs); //有任务时 取出一个任务 头结点
pthread_mutex_unlock(&worker->pool->jobs_mutex); //解锁
job->func(job->user_data, worker->thread); //任务执行函数
}
free(worker);
pthread_exit(NULL);
}
//Thread Pool Create, API
int nThreadPoolCreate(nThreadPool *pool, int numWorkers){
if(numWorkers < 1) numWorkers = 1;
if(pool == NULL) return -1;
memset(pool, 0, sizeof(nThreadPool)); //init pool
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&pool->jobs_cond, &blank_cond, sizeof(pthread_cond_t)); //init jobs_cond
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&pool->jobs_mutex, &blank_mutex, sizeof(pthread_mutex_t)); //init jobs_mutex
for(int i = 0; i < numWorkers; i++){ //创建线程加入线程池
struct N_WORKER *worker = (struct N_WORKER *)malloc(sizeof(struct N_WORKER));
if(worker == NULL){
perror("new");
return -2;
}
memset(worker, 0, sizeof(struct N_WORKER)); //init
worker->pool = pool;
int ret = pthread_create(&worker->thread, NULL, nThreadCallBack, worker); //创建线程
if(ret){
perror("pthread_create");
return -3;
}
LL_ADD(worker,pool->workers); //加入线程池
}
}
void nThreadPoolPush(nThreadPool *pool, struct N_JOB *job){ //添加任务
pthread_mutex_lock(&pool->jobs_mutex);
LL_ADD(job, pool->jobs); //添加
pthread_cond_signal(&pool->jobs_cond); // 唤醒 line:70
pthread_mutex_unlock(&pool->jobs_mutex);
}
int nThreadPoolDestory(nThreadPool *pool){
struct N_WORKER *worker = NULL;
for(worker = pool->workers; worker != NULL; worker = worker->next){
worker->terminate = 1; // 让每个线程终止标致位置1
}
pthread_mutex_lock(&pool->jobs_mutex);
pthread_cond_broadcast(&pool->jobs_cond);
pthread_mutex_unlock(&pool->jobs_mutex);
}
void Job_func(int data ,pthread_t threadId){
std::cout<<"id:"<<threadId<<"->"<<data<<std::endl;
}
int main(){
nThreadPool *pool;
nThreadPoolCreate(pool, 1000);
for(int i = 0; i < 1000; i++){
struct N_JOB *job = (struct N_JOB*)malloc(sizeof(struct N_JOB));
job->func = Job_func;
job->user_data = i;
nThreadPoolPush(pool, job);
}
nThreadPoolDestory(pool);
}