【Linux】线程池

一、概念

1.线程池是一种利用池化技术思想来实现的线程管理技术,主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用已经创建的线程来降低频繁创建和销毁线程所带来的资源消耗。
2.线程池是一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

二、线程池的应用场景

  1. 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB 服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为 Telnet 会话时间比线程的创建时间大多了。
  2. 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。
  3. 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限,出现错误。

三、线程池的优点

1.降低资源消耗 ,复用已创建的线程来降低创建和销毁线程的消耗。
2.提高响应速度 ,任务到达时,可以不需要等待线程的创建立即执行。
3.提高线程的可管理性,使用线程池能够统一的分配、调优和监控。

四、代码

ThreadPool.hpp

cpp 复制代码
#pragma once 
#include<iostream>
#include<string>
#include<vector>
#include<queue>
#include<pthread.h>

#include"Task.hpp"
#include"Thread.hpp"
#include"lockGuard.hpp"


const int N=6;

template<class T>
class ThreadPool
{
    pthread_mutex_t* getlock()
    {
        return &_mutex;
    }
   

    void threadwait()
    {
        //挂起一个线程
        pthread_cond_wait(&_cond,&_mutex);
    }

    void threadwakeup()
    {
        //唤醒一个线程
        pthread_cond_signal(&_cond);
    }

public:
    ThreadPool(int num=N)
    :_num(num)

    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_cond,nullptr);

    }
    ~ThreadPool()
    {
        for(auto& e:_threads)
        {
            e.join();
        }
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
    }

    bool isEmpty()
    {
        return _tasks.empty();
    }

    void init()
    {
        //创建线程
        for(int i=1;i<=_num;++i) 
            //pthread_create(&_threads[i],nullptr,ThreadRoutine,this);
            _threads.push_back(Thread(i,ThreadRoutine,this));
    }
    

    void start()
    {   
        //线程启动
        for(auto& e:_threads)
        {
            e.run();
        }
    }

    void check()
    {
        for(auto& e:_threads)
        {
           std::cout<<"线程ID"<<e.threadid()<<" , "<<e.threadname()<<"is running··· "<<std::endl;
        }

    }
    //放入任务
    void pushtask(const T& task)
    {
        lockGuard lock(&_mutex);
        _tasks.push(task);
        //有新任务进来,唤醒线程去处理
        threadwakeup();
    }

    
    T poptask()
    {
        T t=_tasks.front();
        _tasks.pop();
        return t;
    }
private:
    static void* ThreadRoutine(void* args)
    {   
        pthread_detach(pthread_self());
        //指针强转成线程池对象类型;
        ThreadPool<T>* tp=static_cast<ThreadPool<T>*>(args);
        while(true)
        {
            //1.判断是否有任务
        T t;  //有->处理
        {    //无->等待
            lockGuard lock(tp->getlock());
            //如果任务队列为空,则等待
            while(tp->isEmpty())
            {
               tp->threadwait();
            }
            t=tp->poptask();//从共有区域拿到线程独立栈上;
        }
        
            t();//调用task类里面的仿函数处理任务
            std::cout << "thread handler done, result: " << t.formatRes() << std::endl;
        }
    }
private:
    
    std::vector<Thread> _threads;
    int _num;//线程池里有几个线程;

    std::queue<T> _tasks; // 使用STL的自动扩容的特性

    pthread_mutex_t _mutex;
    pthread_cond_t _cond;
};

Task.hpp

cpp 复制代码
#pragma once
#include<string>
#include<iostream>


class Task
{
public:
    Task(){}
    
    Task(int x,int y,char op):_x(x),_y(y),_op(op),_result(0),_exitCode(0)  {}

    int operator()()
    {
        switch(_op)
        {   
            case '+':
                _result=_x+_y;
                break;
            case '-':
                _result=_x-_y;
                break;
            case '*':
                _result=_x*_y;
                break;
            case '/':
                {
                    if(_y==0) 
                        _exitCode=-1;
                    else   
                        _result=_x/_y;    
                }
                break;
            case '%':
                {
                    if(_y==0) 
                        _exitCode=-2;
                    else   
                        _result=_x%_y;    
                }
                break;
            default:
                break;
        }
    }

    //任务
    std::string formatArg()
    {
        return std::to_string(_x) + _op + std::to_string(_y) + "=?";
    }

    //任务处理结果
     std::string formatRes()
    {
        return std::to_string(_result) + "(" + std::to_string(_exitCode)+ ")";
    }

    ~Task() {}
   


private:
    int _x;
    int _y;
    char _op;
    int _result;
    int _exitCode;
};

lockGuard.hpp

cpp 复制代码
#pragma once

#include <pthread.h>
#include <iostream>

class Mutex//成员:加锁函数和解锁函数
{
public:
    Mutex(pthread_mutex_t* pmutex):_pmutex(pmutex)   {}
    
    void lock()
    {
        pthread_mutex_lock(_pmutex);
    }
   
       void unlock()
    {
        pthread_mutex_unlock(_pmutex);
    }
   

    ~Mutex(){}

private:
    pthread_mutex_t* _pmutex;//需要传入一个互斥量(锁)的指针;
};


//对Mutex进行二次封装;
//创建该对象时自动加锁,析构时自动解锁;
class lockGuard
{   
public:
    lockGuard(pthread_mutex_t* pmutex):_mutex(pmutex)//利用锁的指针构建Mutex对象
    {
        _mutex.lock();
    }

   ~lockGuard()
    {
        _mutex.unlock();
    }

private:
    Mutex _mutex;//类内创建对象
};

Thread.hpp

cpp 复制代码
#pragma once

#include <iostream>
#include <pthread.h>
#include <string>

class Thread
{
public:
    typedef void* (*func_t) (void*);
    typedef enum
    {
        NEW=0,
        RUNNING,
        EXITED
    }ThreadStatus;

public:
      //状态:new,running,exited
        int status()
        {
            return _status;
        }

        //线程名
        std::string threadname()
        {
            return _name;
        }

        //线程ID(共享库中的进程地址空间的虚拟地址)
        pthread_t threadid()
        {
            if(_status==RUNNING)//线程已经被创建,线程id已经输入到成员变量_tid中;
                return _tid;
            else 
            {  
                return 0;
            }
        }

public:

        //构造函数;
        Thread(int num,func_t func,void* args)//num代表第几个线程
        :_tid(0),
        _status(NEW),
        _func(func),
        _args(args)

        {
            char name[128];
            snprintf(name,sizeof(name),"thread-%d",num);
            _name=name;
        }
        
        //析构函数
        ~Thread(){}

      

        //静态成员函数不能访问类内所有成员,因为没有this指针;
        static void* runHelper(void *args)
        {
            Thread* td=(Thread*)args;
            (*td)();//调用仿函数执行线程的回调函数;
            return nullptr; 
        }

        void operator()()//仿函数
        {
            //如果函数指针不为空,则执行该函数指针指向的回调函数;
            if(_func!=nullptr)  _func(_args);
        }

        //创建线程
        void run()
        {
            //因为runHelper函数必须只能有一个void*参数,所以runHelper函数在类内必须定义为static,这样才没有this指针;
            int n=pthread_create(&_tid,nullptr,runHelper,this);
            if(n!=0) return exit(1);//线程创建失败,那么直接退出进程;
            _status=RUNNING;
        }

        //等待线程结束
        void join()
        {
            int n=pthread_join(_tid,nullptr);
            if(n!=0) 
            {
                std::cerr<<"main thread join thread "<<_name<<" error "<<std::endl;
                return;
            }
            _status=EXITED;//线程退出;
        }
       
private:
    pthread_t _tid;//线程ID(原生线程库中为该线程所创建的TCB起始虚拟地址)
    std::string _name;//线程名
    func_t _func;//线程要执行的回调
    void* _args;//线程回调函数参数
    ThreadStatus _status;//枚举类型:状态
};

main.cc

cpp 复制代码
#include<iostream>
#include<memory>
#include"ThreadPool.hpp"



using namespace std;

int main()
{   
    // unique_ptr < ThreadPool<Task> > tp(new ThreadPool<Task>(20));
    // tp->init();
    // tp->start();
    // tp->check();

    //实例化类,并且多次调用getinstance()函数;
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    printf("0X%x\n", ThreadPool<Task>::getinstance());
    while(true)
    {
        // 充当生产者, 从网络中读取数据,构建成为任务,推送给线程池
        int x, y;
        char op;
        std::cout << "please Enter x> ";
        std::cin >> x;
        std::cout << "please Enter y> ";
        std::cin >> y;
        std::cout << "please Enter op(+-*/%)> ";
        std::cin >> op;

        Task t(x,y,op);
        ThreadPool<Task>::getinstance()->pushtask(t); //单例对象也有可能在多线程场景中使用!
    }


    return 0;
}

makefile

cpp 复制代码
ThreadPool:main.cc
	g++ $^ -o $@ -std=c++11 -lpthread
.PHONY:clean
clean:
	rm -f ThreadPool
相关推荐
禁默10 分钟前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Cachel wood16 分钟前
python round四舍五入和decimal库精确四舍五入
java·linux·前端·数据库·vue.js·python·前端框架
Code哈哈笑19 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
gb421528722 分钟前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
程序猿进阶22 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
Youkiup24 分钟前
【linux 常用命令】
linux·运维·服务器
qq_2975046128 分钟前
【解决】Linux更新系统内核后Nvidia-smi has failed...
linux·运维·服务器
zfoo-framework30 分钟前
【jenkins插件】
java
_oP_i33 分钟前
.NET Core 项目配置到 Jenkins
运维·jenkins·.netcore