仿照nginx手写线程池采用c++11实现支持跨平台

3 篇文章 0 订阅
3 篇文章 0 订阅

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;

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值