线程池

线程池

1、首先看个场景:
淘宝双十一,很多人都会买东西,向淘宝发出购物请求—淘宝服务器就要针对这些请求进行处理,服务器上肯定是多执行(流并行 / 并发处理)。
若是请求来了就创建执行流进行处理,存在两个问题:

(1)若峰值压力下,会创建线程过多,有可能导致资源耗尽;
(2)任务处理过程中,线程的创建与销毁成本过高;

所以如何处理呢?
在服务器启动的时候,就创建大量线程 + 创建一个线程安全的队列作为线程池:任务来了,就将任务抛入线程池的任务队列中,线程池中的一个线程会循环去任务队列中获取任务进行处理。

2、线程池的实现:大量的线程+线程安全的任务队列
3、应用场景:大量的数据请求的多任务并发处理。

#include <cstdio>
#include <iostream>
#include <queue>
#include <stdlib.h>
#include <pthread.h>

#include <unistd.h>
#include <time.h>

typedef void (*task_handler_t) (int arg);
class ThreadTask     //任务类
{
    public:
        ThreadTask() {}
        ThreadTask(const int &data, task_handler_t handler):
        _data(data), _handler(handler){  }
        void SetTask(const int &data, task_handler_t handler) {
            _data = data;
            _handler = handler;
        }
        void Run() {
            return _handler(_data);
        }
    private:
        int  _data;
        task_handler_t _handler;
};

#define THREAD_COUNT 5
class ThreadPool
{
    public:
        ThreadPool(int thr_count = THREAD_COUNT):_max_thr(thr_count){
            pthread_mutex_init(&_mutex, NULL);
            pthread_cond_init(&_con_pool, NULL);
            for (int i = 0; i < _max_thr; i++) {
                pthread_t tid;
                //void *thread_routine(void *arg);
                int ret = pthread_create(&tid, NULL, thr_routine, (void*)this);
                if (ret != 0) {
                    std::cerr << "thread create error\n";
                    exit(0);
                }
                pthread_detach(tid);//分离线程--因为我们不关心线程的返回值
            }
        }
        ~ThreadPool(){
            pthread_mutex_destroy(&_mutex);
            pthread_cond_destroy(&_con_pool);
        }
        bool TashPush(const ThreadTask &task) {
            //外部的生产者实现任务入队
            //这里线程池任务队列并没有设置最大节点上限,因此不需要判断阻塞
            pthread_mutex_lock(&_mutex);
            _queue.push(task);
            pthread_mutex_unlock(&_mutex);
            pthread_cond_signal(&_con_pool);//入队之后唤醒一下线程池中的线程

            return true;
        }
        void QueueLock() {
            pthread_mutex_lock(&_mutex);
        }
        bool QueueIsEmpty() {
            return _queue.empty();
        }
        void ThreadWait() {
            pthread_cond_wait(&_con_pool, &_mutex);
        }
        bool TaskPop(ThreadTask *task) {
            *task = _queue.front();
            _queue.pop();
            return true;
        }
        void QueueUnlock() {
            pthread_mutex_unlock(&_mutex);
        }
    private:
        //这个函数一个类成员函数,有一个隐藏参数this指针,因此需要定义成为静态函数
        //这是一个静态函数,没有this指针,导致无法直接访问类的内部成员变量
        //通过参数传入当前的对象this指针,进而访问对象的公有成员变量以及成员函数
        static void *thr_routine(void *arg) {
            ThreadPool *pool = (ThreadPool*)arg;
            while(1) {
                //循环从队列获取任务进行处理
                pool->QueueLock();//类外无法直接访问类的私有成员,因此需要通过公有接口实现操作
                while(pool->QueueIsEmpty()) {//访问_queue是否为NULL
                    pool->ThreadWait(); //访问_con_pool以及_mutex
                }
                ThreadTask task;
                pool->TaskPop(&task); // 访问_queue
                pool->QueueUnlock();  // 访问_mutex
                //任务的处理一定要放在解锁之后完成,
                //否则,如果任务处理时间过长会导致其它线程获取不到任务
                task.Run();//通过run接口直接通过用户传入的方法完成数据处理
            }
            return NULL;
        }
    private:
        int _max_thr;//线程的数量
        std::queue<ThreadTask> _queue;//任务队列
        pthread_mutex_t _mutex; //实现队列的互斥操作
        pthread_cond_t _con_pool;//线程池中的线程等待队列
};

//这个函数就是针对一个数据要传入线程池的处理方法
void test(int data)
{
    srand(time(NULL));
    int sec = rand()%5;
    printf("thread:%p:%d sleep %d sec\n", pthread_self(), data, sec);
    sleep(sec);
}
int main()
{
    ThreadPool pool;
    for (int i = 0; i < 10; i++) {
        ThreadTask task;
        task.SetTask(i, test);// Run() {test(i)}
        pool.TashPush(task);
    }
    while(1) {
        sleep(1);
    }
    return 0;
}

1、注意的思路;

  • 1.通过任务类,实现向线程池抛入任务的时候,既抛入数据也抛入处理方法的思路。

  • 2.线程池只需要向外提供一个任务入队接口即可,任务的处理都是在线程池内部完成的。

  • 3.线程池中的线程都只需要获取任务对象之后调用Run接口就可以实现任务处理。

2、注意事项:

1.线程创建函数

pthread_create要求传入的入口函数只有一个参数 
void (*thread_routine)(void *arg);
但是若入口函数是一个类的成员函数,则默认会有一个隐藏参数this指针导致函数参数
类型不匹配;因此需要将这个入口函数在类内定义为静态函数.

2.静态函数无法直接访问线程池对象;因此将这个对象的this指针通过线程入口函数传入;在线程内部就可以访问到这个对象了;但是类外使用,因此无法直接访问对象的私有成员,因此对私有成员的访问都需要通过公有接口来实现。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值