线程池
上图是一个线程池组件
线程池主要分三块东西,一个工作线程队列,一个任务队列还有一个管理组件(互斥锁和条件变量)
生活中很多场景和线程池比较类似
比如去银行取款,一个柜台就是一个工作线程,一个人是一个任务,看号码的窗口就是管理组件
再比如去食堂吃饭,一个打饭阿姨就是一个工作线程
下面实现了一个线程池组建
代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
//添加一个item到测试队列里面
#define LL_ADD(item, list) do { \
item->prev = NULL; \
item->next = list; \
list = item; \
} while(0)
//从测试队列里面删除一个item
#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 (list == item) list = item->next; \
item->prev = item->next = NULL; \
} while(0)
//工作线程
typedef struct NWORKER {
pthread_t thread;//线程号
int terminate;//是否结束
struct NWORKQUEUE *workqueue;//工作队列指针
struct NWORKER *prev;//上面一个工作线程指针
struct NWORKER *next;//下一个工作线程指针
} nWorker;
//任务item,双向链表
typedef struct NJOB {
void (*job_function)(struct NJOB *job);//回调函数
void *user_data;//用户数据
struct NJOB *prev;//前一个任务的指针
struct NJOB *next;//后一个任务的指针
} nJob;
typedef struct NWORKQUEUE {
struct NWORKER *workers; //头结点指针
struct NJOB *waiting_jobs; //等待任务队列头指针
pthread_mutex_t jobs_mtx; //互斥锁
pthread_cond_t jobs_cond; //条件变量
} nWorkQueue;//工作队列
typedef nWorkQueue nThreadPool;
//工作线程执行函数
static void *ntyWorkerThread(void *ptr) {
//拿出参数
nWorker *worker = (nWorker*)ptr;
while (1) {
//上锁
pthread_mutex_lock(&worker->workqueue->jobs_mtx);
while (worker->workqueue->waiting_jobs == NULL) {
if (worker->terminate) break;
//条件等待
pthread_cond_wait(&worker->workqueue->jobs_cond, &worker->workqueue->jobs_mtx);
}
//如果该线程要结束,则解锁退出
if (worker->terminate) {
pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
break;
}
//取第一个任务
nJob *job = worker->workqueue->waiting_jobs;
//移除任务队列中的任务
if (job != NULL) {
LL_REMOVE(job, worker->workqueue->waiting_jobs);
}
//解锁
pthread_mutex_unlock(&worker->workqueue->jobs_mtx);
if (job == NULL) continue;
//执行回调函数
job->job_function(job);
}
//退出,释放内存
free(worker);
pthread_exit(NULL);
}
//创建线程池
int ntyThreadPoolCreate(nThreadPool *workqueue, int numWorkers) {
//检测参数
if (numWorkers < 1) numWorkers = 1;
memset(workqueue, 0, sizeof(nThreadPool));
//初始化条件变量并拷贝过去
pthread_cond_t blank_cond = PTHREAD_COND_INITIALIZER;
memcpy(&workqueue->jobs_cond, &blank_cond, sizeof(workqueue->jobs_cond));
//初始化互斥锁并拷贝过去
pthread_mutex_t blank_mutex = PTHREAD_MUTEX_INITIALIZER;
memcpy(&workqueue->jobs_mtx, &blank_mutex, sizeof(workqueue->jobs_mtx));
//构建工作线程
int i = 0;
for (i = 0;i < numWorkers;i ++) {
//分配内存
nWorker *worker = (nWorker*)malloc(sizeof(nWorker));
if (worker == NULL) {
perror("malloc");
return 1;
}
memset(worker, 0, sizeof(nWorker));
//绑定工作线程与线程池
worker->workqueue = workqueue;
//创建线程 绑定函数
int ret = pthread_create(&worker->thread, NULL, ntyWorkerThread, (void *)worker);
if (ret) {
perror("pthread_create");
free(worker);
return 1;
}
//将工作线程加入到工作线程队列中
LL_ADD(worker, worker->workqueue->workers);
}
return 0;
}
//关闭线程池
void ntyThreadPoolShutdown(nThreadPool *workqueue) {
nWorker *worker = NULL;
//把terminate设置为true
for (worker = workqueue->workers;worker != NULL;worker = worker->next) {
worker->terminate = 1;
}
//上锁
pthread_mutex_lock(&workqueue->jobs_mtx);
workqueue->workers = NULL;
workqueue->waiting_jobs = NULL;
pthread_cond_broadcast(&workqueue->jobs_cond);
//解锁
pthread_mutex_unlock(&workqueue->jobs_mtx);
}
//把任务加入到任务队列中
void ntyThreadPoolQueue(nThreadPool *workqueue, nJob *job) {
pthread_mutex_lock(&workqueue->jobs_mtx);
LL_ADD(job, workqueue->waiting_jobs);
//有任务了 发送信号
pthread_cond_signal(&workqueue->jobs_cond);
pthread_mutex_unlock(&workqueue->jobs_mtx);
}
#if 1
#define KING_MAX_THREAD 80
#define KING_COUNTER_SIZE 1000
//任务的回调函数
void king_counter(nJob *job) {
int index = *(int*)job->user_data;
printf("index : %d, selfid : %lu\n", index, pthread_self());
free(job->user_data);
free(job);
}
int main(int argc, char *argv[]) {
//初始化线程池
nThreadPool pool;
//初始化各个组件
ntyThreadPoolCreate(&pool, KING_MAX_THREAD);
int i = 0;
for (i = 0;i < KING_COUNTER_SIZE;i ++) {
nJob *job = (nJob*)malloc(sizeof(nJob));
if (job == NULL) {
perror("malloc");
exit(1);
}
//设置回调函数
job->job_function = king_counter;
job->user_data = malloc(sizeof(int));
*(int*)job->user_data = i;
//把任务加入到任务队列中
ntyThreadPoolQueue(&pool, job);
}
getchar();
printf("\n");
}
#endif