一个简单的c++线程池的封装

首先问三个问题:什么是线程池?线程池的优势是什么?实现原理是什么?


1.线程池的概念

字面意思就是”一个存放线程的池塘”,类似于内存池的概念,创建固定数量的线程,来处理多个任务,线程执行完一个任务后不销毁而是继续执行下一个任务,这样就避免了线程的频繁创建和销毁,大大提高了效率(和多线程相模型相比),这也回答了第二个问题,线程池的优势所在。

2.线程池实现原理

线程池不只有一个线程组,还需要一个任务队列来方便线程池获取任务,此任务队列为被所有线程共享,因此在考虑类的数据结构是必须添加进来,另外,因为有多个线程共享一个资源的情况(都会访问任务队列),还必须要考虑加锁的问题。借用一张图来说明线程池的工作流程:
线程工作原理

首先是线程同步的类封装condition.h:

#ifndef MYTHREADPOOL_CONDITION_H
#define MYTHREADPOOL_CONDITION_H

#include <pthread.h>

//封装一个互斥量和条件变量作为状态
typedef struct condition
{
    pthread_mutex_t pmutex;
    pthread_cond_t pcond;
}condition_t;

class condition_mutex
{
public:
    condition_mutex();
    ~condition_mutex();
    //对状态的操作函数
    int condition_init();//初始化
    int condition_lock();//加锁
    int condition_unlock();//解锁
    int condition_wait();//等待
    int condition_timedwait(const struct timespec *abstime);//超时等待
    int condition_signal();//唤醒一个睡眠线程
    int condition_broadcast();//唤醒所以睡眠线程
    int condition_destroy();//销毁
private:
    condition_t c_mutex;
};

#endif //MYTHREADPOOL_CONDITION_H

具体实现condition.cpp:

#include "condition.h"

condition_mutex::condition_mutex()
{
    condition_init();
}

condition_mutex::~condition_mutex()
{
    condition_destroy();
}

//初始化
int condition_mutex::condition_init()
{
    int status;
    if((status = pthread_mutex_init(&c_mutex.pmutex, NULL)))
        return status;

    if((status = pthread_cond_init(&c_mutex.pcond, NULL)))
        return status;

    return 0;
}

//加锁
int condition_mutex::condition_lock()
{
    return pthread_mutex_lock(&c_mutex.pmutex);
}

//解锁
int condition_mutex::condition_unlock()
{
    return pthread_mutex_unlock(&c_mutex.pmutex);
}

//等待
int condition_mutex::condition_wait()
{
    return pthread_cond_wait(&c_mutex.pcond,&c_mutex.pmutex);
}

//固定时间等待
int condition_mutex::condition_timedwait(const struct timespec *abstime)
{
    return pthread_cond_timedwait(&c_mutex.pcond,&c_mutex.pmutex, abstime);
}

//唤醒一个睡眠线程
int condition_mutex::condition_signal()
{
    return pthread_cond_signal(&c_mutex.pcond);
}

//唤醒所有睡眠线程
int condition_mutex::condition_broadcast()
{
    return pthread_cond_broadcast(&c_mutex.pcond);
}

//释放
int condition_mutex::condition_destroy()
{
    int status;
    if((status = pthread_mutex_destroy(&c_mutex.pmutex)))
        return status;

    if((status = pthread_cond_destroy(&c_mutex.pcond)))
        return status;

    return 0;
}

接下来是线程池结构的封装threadpool.h:

#ifndef MYTHREADPOOL_THREADPOOL_H
#define MYTHREADPOOL_THREADPOOL_H

//线程池头文件

#include "condition.h"

//封装线程池中的对象需要执行的任务对象
typedef struct task
{
    void *(*run)(void *args);  //函数指针,需要执行的任务
    void *arg;              //参数
    struct task *next;      //任务队列中下一个任务
}task_t;


//下面是线程池结构体
typedef struct threadpool_data
{
    condition_mutex ready;    //状态量
    task_t *first;       //任务队列中第一个任务
    task_t *last;        //任务队列中最后一个任务
    int counter;         //线程池中已有线程数
    int idle;            //线程池中空闲线程数
    int max_threads;     //线程池最大线程数
    int quit;            //是否退出标志
}threadpool_t;

class threadpool
{
public:
    threadpool(int threads);
    ~threadpool();
    //线程池初始化
    void threadpool_init(int threads);

//往线程池中加入任务
    void threadpool_add_task(void *(*run)(void *arg), void *arg);

//摧毁线程池
    void threadpool_destroy();

private:
    threadpool_t workers;
};


#endif //MYTHREADPOOL_THREADPOOL_H

线程池实现threadpool.cpp:

#include "threadpool.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>

//创建的线程执行
static void *thread_routine(void *arg)
{
    struct timespec abstime;
    int timeout;//超时标志位
    printf("thread %d is starting\n",pthread_self());
    threadpool_t *pool = (threadpool_t *)arg;
    while(1)
    {
        timeout = 0;
        //访问线程池之前需要加锁
        pool->ready.condition_lock();
        //空闲
        pool->idle++;
        //等待队列有任务到来 或者 收到线程池销毁通知
        while(pool->first == NULL && !pool->quit)
        {
            //否则线程阻塞等待
            printf("thread %d is waiting\n",pthread_self());
            //获取从当前时间,并加上等待时间, 设置线程的超时睡眠时间
            clock_gettime(CLOCK_REALTIME, &abstime);
            abstime.tv_sec += 2;
            int status;
            status = pool->ready.condition_timedwait(&abstime);//该函数会解锁,允许其他线程访问,当被唤醒时,加锁
            if(status == ETIMEDOUT)
            {
                printf("thread %d wait timed out\n",pthread_self());
                timeout = 1;
                break;
            }
        }

        pool->idle--;
        if(pool->first != NULL)
        {
            //取出等待队列最前的任务,移除任务,并执行任务
            task_t *t = pool->first;
            pool->first = t->next;
            //由于任务执行需要消耗时间,先解锁让其他线程访问线程池
            pool->ready.condition_unlock();
            //执行任务
            t->run(t->arg);
            //执行完任务释放内存
            free(t);
            //重新加锁
            pool->ready.condition_lock();
        }

        //退出线程池
        if(pool->quit && pool->first == NULL)
        {
            pool->counter--;//当前工作的线程数-1
            //若线程池中没有线程,通知等待线程(主线程)全部任务已经完成
            if(pool->counter == 0)
            {
                pool->ready.condition_signal();
            }
            pool->ready.condition_unlock();
            break;
        }
        //超时,跳出销毁线程
        if(timeout == 1)
        {
            pool->counter--;//当前工作的线程数-1
            pool->ready.condition_unlock();
            break;
        }

        pool->ready.condition_unlock();
    }

    printf("thread %d is exiting\n", pthread_self());
    return NULL;

}

threadpool::threadpool(int threads)
{
    threadpool_init(threads);
}

threadpool::~threadpool()
{
    threadpool_destroy();
}
//线程池初始化
void threadpool::threadpool_init(int threads)
{
    workers.ready.condition_init();
    workers.first = NULL;
    workers.last =NULL;
    workers.counter =0;
    workers.idle =0;
    workers.max_threads = threads;
    workers.quit =0;
}


//增加一个任务到线程池
void threadpool::threadpool_add_task(void *(*run)(void *arg), void *arg)
{
    //产生一个新的任务
    task_t *newtask = (task_t *)malloc(sizeof(task_t));
    newtask->run = run;
    newtask->arg = arg;
    newtask->next=NULL;//新加的任务放在队列尾端

    //线程池的状态被多个线程共享,操作前需要加锁
    workers.ready.condition_lock();

    if(workers.first == NULL)//第一个任务加入
    {
        workers.first = newtask;
    }
    else
    {
        workers.last->next = newtask;
    }
    workers.last = newtask;  //队列尾指向新加入的线程

    //线程池中有线程空闲,唤醒
    if(workers.idle > 0)
    {
        workers.ready.condition_signal();
    }
        //当前线程池中线程个数没有达到设定的最大值,创建一个新的线程
    else if(workers.counter < workers.max_threads)
    {
        pthread_t tid;
        pthread_create(&tid, NULL, thread_routine, &workers);
        workers.counter++;
    }
    //结束,访问
    workers.ready.condition_unlock();
}

//线程池销毁
void threadpool::threadpool_destroy()
{
    //如果已经调用销毁,直接返回
    if(workers.quit)
    {
        return;
    }
    //加锁
    workers.ready.condition_lock();
    //设置销毁标记为1
    workers.quit = 1;
    //线程池中线程个数大于0
    if(workers.counter > 0)
    {
        //对于等待的线程,发送信号唤醒
        if(workers.idle > 0)
        {
            workers.ready.condition_broadcast();
        }
        //正在执行任务的线程,等待他们结束任务
        while(workers.counter)
        {
            workers.ready.condition_wait();
        }
    }
    workers.ready.condition_unlock();
    workers.ready.condition_destroy();
}

测试程序:

#include "threadpool.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

void* mytask1(void *arg)
{
    printf("thread %d is working on task %d\n", pthread_self(), *(int*)arg);
    sleep(1);
    free(arg);
    return NULL;
}

void* mytask2(void *arg)
{
    printf("thread %d is working on task %d\n", pthread_self(), *(int*)arg);
    sleep(10);
    free(arg);
    return NULL;
}

//测试代码
int main(void)
{
    //初始化线程池,最多三个线程
    threadpool pool(3);

    int i;
    //创建十个任务
    for(i=0; i < 10; i++)
    {
        if(i == 5)
        {
            sleep(5);//故意停顿5秒钟,测试线程超时
        }

        int *arg = (int *)malloc(sizeof(int));
        *arg = i;
        if (i%2 == 0)
        {
            pool.threadpool_add_task(mytask1, arg);//偶数执行任务1
        }
        else
        {
            pool.threadpool_add_task(mytask2, arg);//奇数执行任务2
        }
    }
    return 0;
}

运行结果分析:
运行结果分析

在本测试程序中,主线程(即进程本身)连续创建了三个线程,6139904(暂记为1号线程),6676480(2号线程),7213056(3号线程),且不停地在向任务队列中添加任务,从图中看到,1号线程执行了任务0,2号线程执行了任务1,3号线程执行了任务2,由于奇数任务的执行时间较长(mytask2,休眠10秒钟),1号线程和3号线程结束的比较快,又开始获取新的任务,3号线程获取任务3,进入长时间的休眠中,此时1号线程处理完任务4之后,继续处理任务5,发现任务队列已空,且轮询等待2秒之后没有获取到新的任务,退出线程并销毁。之后5号任务加入了队列,线程池又重新创建了7213056(4号线程),……
从线程的执行时序可以看出,本线程池很好的完成了多任务的线程分配,且执行没有问题,封装简单易用。

参考原文:
https://www.cnblogs.com/yangang92/p/5485868.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值