1、为了方便线程和任务的管理,我们分别将任务和线程用双向链表串起来。
下面介绍关于线程节点及任务节点的数据结构
线程节点
typedef struct NWORKER {
thread threadid;
int terminate; //控制退出
struct NWORKER* next;//把线程串起来方便管理退出
struct NWORKER* prev;
struct NMANAGER* pool; //每个线程要知道自己所属的池子的相关信息
}nWorker,*PnWorker;
任务节点
typedef struct NJOB {
void (*func)(void* arg); //每个任务自己的回调函数
void* userdata; //每个任务的数据
int jobId; //每个任务的id
struct NJOB* next; //把任务串起来
struct NJOB* prev;
}nJob,*PnJob;
//单独拿出来
std::mutex jobs_mtx;
std::condition_variable jobs_cond; //条件变量 无任务的时候挂起
//管理组件--线程池 也如上图线程包含了这个线程列表和任务列表
typedef struct NMANAGER {
PnWorker workers; //指向线程双向链表
PnJob jobs; //指向任务双向链表
}nManager,*PnManager;
上面就是所需要的所有的数据结构了
下面我们来看一下怎么把线程池构建起来
看着上面的线程池数据结构,要构建线程池我们肯定需要先将两个双向链表构建起来:
定义两个宏定义:
头部插入法往list链表里面增加item节点
#define LL_ADD(item , list)do { \
item->next = list; \
item->prev = NULL; \
if(list!=NULL)list->prev = item;\ 这个地方很重要 添加第一个节点的时候list为空
list = item; \
}while (0)
在list链表里面删除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)
有了上面两个方法我们就可以构建线程池了 外部需要传入我们线程池里面的线程数量
创建线程池
int nThreadPoolCreate(PnManager pool, int numWorkers)
{
if (pool == NULL)return -2;
if (numWorkers < 1) numWorkers = 1;
memset(pool, 0, sizeof(PnManager));
int i = 0;
for (i = 0; i < numWorkers; i++) {
PnWorker worker = new NWORKER();
worker->terminate = 0;
worker->pool = pool;//赋值线程池给子节点
if (worker == NULL) {
perror("malloc");
return 1;
}
worker->threadid = thread(nWorkerCallback, worker);
worker->threadid.detach();
LL_ADD(worker, pool->workers);//第一次加入的时候worker是空,所以LL_ADD需判断
printf("LL_ADD worker thread = %d\n", i);
}
return 0;
}
线程还需要个回调函数
//线程回调函数
void* nWorkerCallback(void* arg) {
PnWorker worker = (PnWorker)arg;
while (1)
{
unique_lock<std::mutex>lk(jobs_mtx);
while (worker->pool->jobs==nullptr)
{
if (worker->terminate == 1) {
lk.unlock();
return 0; //线程退出
}
jobs_cond.wait(lk);
}
if (worker->terminate == 1) {
lk.unlock();
break; //线程退出
}
//取任务
PnJob job = worker->pool->jobs;
LL_REMOVE(job, worker->pool->jobs);
//调用任务自己的回调函数
if (job == NULL)continue;
job->func(job);
}
return 0;
}
上面用到了任务回调函数 这里我们定义一个任务回调函数
void doTask(void* userdata) {
nJob* job = (nJob*)userdata;
printf("index : %d,threadId \n", job->jobId);
free(job);
}
下面我们要完成任务添加的函数封装
int nThreadPoolPushJob(PnManager pool, nJob* job) {
unique_lock<std::mutex>lk(jobs_mtx);
LL_ADD(job, pool->jobs); //来个任务唤醒一个线程
jobs_cond.notify_one();
return 1;
}
线程销毁函数
void destory(PnManager pool)
{
PnWorker workers = pool->workers;
for (workers; workers != NULL; workers = workers->next) {
workers->terminate = 1;
}
//通知所有线程退出
jobs_cond.notify_all();
pool->jobs = NULL;
pool->workers = NULL;
}
void SleepFor(int tw) {
std::chrono::milliseconds t(tw);
std::this_thread::sleep_for(t);
}
测试main函数
int main()
{
//创建线程池
PnManager threadpool = new nManager();
nThreadPoolCreate(threadpool, 5);//开启12个线程
//2 继续添加任务
for (int i = 0; i < 1000; ++i)
{
nJob* job = (nJob*)malloc(sizeof(nJob));
//job->userdata =new int();
job->userdata = &i;
job->jobId = i;
job->func = doTask;
nThreadPoolPushJob(threadpool, job);
}
//主线程等待任务执行完毕
SleepFor(2000);
//退出线程
destory(threadpool);
//等待子线程退出完毕
SleepFor(50);
return 0;
}