Linux:多线程-线程池

理解线程池存在的意义

如果有多个用户多次而频繁的请求操作,服务器上每次就会创建一个线程为该用户的该次操作提供服务,若每个任务的执行时间也很短,那么创建线程和销毁线程所用事件占总共时长的比重就会增大。为了出现这种情况得到优化,因此,线程池出现了。

线程池的实现

  1. 得首先创建一些线程。
  2. 得有一个队列,线程得从这个队列中获取需要执行的任务。
  3. 要封装存在队列中的任务,提供好用的接口。

所需头文件

#include <iostream>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <queue>
using namespace std;

设计任务类

需求

  1. 可以通过固定接口传递用户需要执行的任务。
  2. 提供接口可以让用户传递所需要执行的数据。
  3. 提供接口让用户可以重置所需要执行的任务。
  4. 任务执行接口直接写在该类中,线程只需要调用接口就可以,减少了线程池代码量。

代码实现

//typedef一个函数指针
typedef void (*TaskHandler)(int data);

class ThreadTask
{
    private:
        int _data;//任务参数
        TaskHandler _handler;//任务
    public:
        ThreadTask(int data,TaskHandler handler):_data(data),_handler(handler)
    {}
        ThreadTask(){}
        //任务重置接口
        bool TaskSet(int data,TaskHandler handler)
        {
            _data = data;
            _handler = handler;
            return true;
        }
        //任务执行接口
        bool TaskRun()
        {
            _handler(_data);
            return true;
        }
};

设计线程池类

需求

  1. 不能直接在构造函数中创建线程,不然发生错误无法返回信息。
  2. 得有队列,直接使用stl里面现成的就好。
  3. 提供添加任务接口。
  4. 提供一个互斥锁和一个条件变量,避免多个线程对同一个任务的争抢。

代码实现

#define MAX_THR 4
class ThreadPool
{
    private:
        std::queue<ThreadTask> task_queue;
        int thr_num;
        pthread_mutex_t _mutex;
        pthread_cond_t _cond_con;
        pthread_cond_t _cond_pro;
    public:
    	//以下这几个函数,只是提供接口供静态函数调用私有成员
        void ThreadLock()
        {
            pthread_mutex_lock(&_mutex);
        }
        void ThreadUnlock()
        {
            pthread_mutex_unlock(&_mutex);
        }

        bool QueueEmpty()
        {
            return task_queue.empty();
        }
        void ThreadWait()
        {
            pthread_cond_wait(&_cond_con,&_mutex);
        }
        bool QueuePop(ThreadTask &tt)
        {
            tt = task_queue.front();
            task_queue.pop();
            return true;
        }

    private:
        //必须设置为静态函数,因为如果是成员函数,参数中有this指针,不符合接口
        static void *thr_start(void *arg)
        {
            ThreadPool* pool = (ThreadPool*)arg;
            while(1)
            {
                pool->ThreadLock();
                while(pool->QueueEmpty())
                {
                    pool->ThreadWait();
                }
                ThreadTask tt;
                pool->QueuePop(tt);
                pool->ThreadUnlock();
                tt.TaskRun();
            }
            return NULL;
        }
    public:
        ThreadPool(int max = MAX_THR):thr_num(max)
    {}
        //设置初始化接口的原因是,构造函数无法获取返回值,若创建线程失败我们则无从得知。
        //1.创建线程
        //2.初始化互斥锁和条件变量
        bool PoolInit()
        {
            pthread_t tid[MAX_THR];
            pthread_mutex_init(&_mutex,NULL);
            pthread_cond_init(&_cond_con,NULL);
            pthread_cond_init(&_cond_pro,NULL);
            for(int i = 0; i<thr_num;i++)
            {
                int ret = pthread_create(&tid[i],NULL,thr_start,(void*)this);
                if(ret!=0)
                {
                    printf("create error\n");
                    return false;
                }
                pthread_detach(tid[i]);
            }
            return true;
        }
        //设置插入接口的目的是为了让用户插入所需要处理的代码
        bool TaskPush(ThreadTask &tt)
        {
            pthread_mutex_lock(&_mutex);
            task_queue.push(tt);
            pthread_mutex_unlock(&_mutex);
            pthread_cond_signal(&_cond_con);
            return true;
        }
};

测试

设置一个函数,用来输出一串关于参数的信息,放进线程池中多次执行。

设置测试函数

void test(int data)
{
    printf("this is %p --get data:%d \n",pthread_self(),data);
    sleep(1);//睡眠一秒,方便查看
}

主函数调用

int main()
{
    ThreadPool pool;
    
    //初始化线程池
    pool.PoolInit();
    //预期打印出十行数据
    for(int i = 0;i<10 ;i++)
    {
        ThreadTask task(i,test);
        pool.TaskPush(task);
    }
    while(1)
    {
        sleep(1);
    }
    return 0;
}

结果

在这里插入图片描述

发布了35 篇原创文章 · 获赞 13 · 访问量 2113

猜你喜欢

转载自blog.csdn.net/weixin_42458272/article/details/103340870