【Linux】基于单例模式懒汉实现方式的线程池


需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。


 目录

一、LockGuard.hpp

二、Task.hpp 

三、Thread.hpp

四、ThreadPool.hpp


一、LockGuard.hpp

#pragma once
#include <iostream>
#include <pthread.h>
class Mutex//锁的对象
{
public:
    Mutex(pthread_mutex_t* lock_p=nullptr)
    :_lock_p(lock_p)
    {}
    ~Mutex()
    {}
    void lock()
    {
        if(_lock_p)
        {
            pthread_mutex_lock(_lock_p);
        }
    }
    void unlock()
    {
        if(_lock_p)
        {
            pthread_mutex_unlock(_lock_p);
        }
    }
private:
    pthread_mutex_t* _lock_p;//锁的指针
};
class LockGuard//加锁和解锁的类
{
public:
    LockGuard(pthread_mutex_t* mutex)
    :_mutex(mutex)
    {
        _mutex.lock();//在构造函数进行加锁
    }
    ~LockGuard()
    {
        _mutex.unlock();//在析构函数进行解锁
    }
private:
    Mutex _mutex;
};

二、Task.hpp 

#pragma once
#include <iostream>
#include <functional>
#include <string>
class Task
{
    //using func=std::function<int(int,int,char)>;
    typedef std::function<int(int,int,char)> func_t;//函数对象
public:
    Task()
    {}
    Task(int x,int y,char op,func_t func)
    :_x(x)
    ,_y(y)
    ,_op(op)
    ,_callBack(func)
    {}
    std::string operator()()//消费者调用
    {
        int result=_callBack(_x,_y,_op);
        char buffer[1024];
        snprintf(buffer,sizeof(buffer),"%d %c %d=%d",_x,_op,_y,result);//结果字符串
        return buffer;
    }
    std::string toTaskString()//生产者调用
    {
        char buffer[1024];
        snprintf(buffer,sizeof(buffer),"%d %c %d=?",_x,_op,_y);
        return buffer;
    }
private:
    int _x;
    int _y;
    char _op;//加减乘除取模
    func_t _callBack;//回调函数
};

const std::string oper = "+-*/%";
int myMath(int x, int y, char op)
{
    int result = 0;
    switch (op)
    {
    case '+':
        result = x + y;
        break;
    case '-':
        result = x - y;
        break;
    case '*':
        result = x * y;
        break;
    case '/':
    {
        if (y == 0)
        {
            std::cerr << "div zero error" << std::endl;
            result = -1;
        }
        else
            result = x / y;
    }
        break;
    case '%':
    {
        if (y == 0)
        {
            std::cerr << "mod zero" << std::endl;
            result = -1;
        }
        else
            result = x % y;
    }
    break;
    default:
        std::cout<<"运算符输入有误"<<std::endl;
        break;
    }
    return result;
}

三、Thread.hpp

#pragma once
#include <iostream>
#include <pthread.h>
#include <string>
#include <functional>
#include <cassert>
namespace ThreadNs
{
    typedef std::function<void*(void*)> func_t;//定义一个函数对象类型,它的返回值和参数都是void*
    const int num = 1024;

    class Thread
    {
    private:
        //因为形参有个this指针,所以弄成static
        static void* start_routine(void* args)//这里的args就是 start()的ctx
        {
            Thread* _this=static_cast<Thread*>(args);
            return _this->callback();
        } 
        void* callback()
        {
            return _func(_args);
        }
    public:
        //构造函数
        Thread()
        {
            char nameBuffer[num];
            snprintf(nameBuffer,sizeof(nameBuffer),"thread-%d",threadNum++);
            _name=nameBuffer;
        }
        void start(func_t func,void* args=nullptr)//线程启动
        {
            _func=func;
            _args=args;
            int n=pthread_create(&_tid,nullptr,start_routine,this);
            assert(n==0);
            (void)n;
        }
        void join()
        {
            int n=pthread_join(_tid,nullptr);
            assert(n==0);
            (void)n;
        }
        std::string threadName()
        {
            return _name;
        }
        ~Thread()
        {}
    private:
        std::string _name;//线程的名字
        func_t _func;//线程的回调函数
        void* _args;//喂给线程的参数
        pthread_t _tid;//线程ID
        static int threadNum;//线程编号,最好自己再加个锁
    };
    int Thread::threadNum=1;
    //异常和if。意料之外
    //assert。意料之中。99%概率为真。
}

四、ThreadPool.hpp

#pragma once
#include <vector>
#include <queue>
#include <pthread.h>
#include <unistd.h>
#include <mutex>
#include "Thread.hpp"
#include "LockGuard.hpp"
using namespace ThreadNs;
const int gnum =5;

template <class T>//声明
class ThreadPool;

template <class T>
struct ThreadData
{
    ThreadData(ThreadPool<T>* tp,const std::string& s)
    :_threadPool(tp)
    ,_name(s)
    {}
    ThreadPool<T>* _threadPool;
    std::string _name;
};
template <class T>
class ThreadPool
{
private:
    //因为普通成员函数第一个参数是this指针,和回调方法不匹配,故改成static类型
    static void* handlerTask(void* args)//args是ThreadData对象指针
    {
        ThreadData<T>* td=static_cast<ThreadData<T>*>(args);
        while(1)
        {
            T t;
            {   //RAII,出了作用域LockGuard会销毁,将析构锁
                LockGuard lockGuard(td->_threadPool->mutex());//加锁
                while(td->_threadPool->IsQueueEmpty())//如果队列为空,则等待
                {
                    td->_threadPool->ThreadWait();
                }
                //线程能走到这里,说明队列一定有任务给线程
                t=td->_threadPool->Pop();//从队列中取出任务
            }
            std::cout<<td->_name<<"已获取任务:"<<t.toTaskString()<<"处理结果是:"<<t()<<std::endl;//处理任务,这个任务放到线程的外面
        }
        delete td;//析构ThreadData对象
        return nullptr;
    }
    ThreadPool(const int& num=gnum)
    :_num(num)
    {
        pthread_mutex_init(&_mutex,nullptr);
        pthread_cond_init(&_cond,nullptr);
        //创建线程
        for(int i=0;i<_num;++i)
        {
            _threads.push_back(new Thread());
        }
    }
    ThreadPool(const ThreadPool<T>&)=delete;//禁用拷贝构造
    ThreadPool<T>& operator=(const ThreadPool<T>&)=delete;//禁用赋值运算符重载

public://解决静态handlerTask是静态函数的问题,这几个都是偷家函数
    void LockQueue()   {pthread_mutex_lock(&_mutex);}
    void UnLockQueue() {pthread_mutex_unlock(&_mutex);}
    bool IsQueueEmpty(){return _taskQueue.empty();}
    void ThreadWait()  {pthread_cond_wait(&_cond,&_mutex);}
    T Pop()         
    {
        T t=_taskQueue.front();
        _taskQueue.pop();
        return t;
    } 
    pthread_mutex_t* mutex()
    {
        return &_mutex;
    }
public: 
    void run()//线程启动
    {
        for(const auto& t:_threads)
        {
            ThreadData<T>* td=new ThreadData<T>(this,t->threadName());
            t->start(handlerTask,(void*)td);
            std::cout<<t->threadName()<<"start..."<<std::endl;
        }
    }
    void push(const T& in)
    {
        //RAII,出了作用域,锁将会被释放
        LockGuard lockGuard(&_mutex);
        _taskQueue.push(in);
        pthread_cond_signal(&_cond);
        std::cout<<"任务发送成功"<<std::endl;
    }
    ~ThreadPool()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_cond);
        for(const auto& t:_threads)
        {
            delete t;
        }
    }
    static ThreadPool<T>* getInstance()//这里的static的作用是让这个函数只有一份,获取单例对象。tp是临界资源,需要加锁
    {
        if(nullptr==tp)//因为锁只创建一次,防止线程进来被锁阻塞
        {
            //只进来一次就够了
            _singletonLock.lock();
            if(nullptr==tp)//说明对象还没有被创建
            {
                tp=new ThreadPool<T>(); 
            }
            _singletonLock.unlock();
        }
        return tp;
    }
private:
    int _num;//线程个数
    std::vector<Thread*> _threads;//使用vector存放线程
    std::queue<T> _taskQueue;//任务队列,往里面放任务,它是共享资源,需要加锁保护
    pthread_mutex_t _mutex;//互斥锁
    pthread_cond_t _cond;//条件变量

    static ThreadPool<T>* tp;//单例模式静态的对象指针
    static std::mutex _singletonLock;//获取单例对象使用的锁

};
template <class T>
ThreadPool<T>* ThreadPool<T>::tp=nullptr;

template <class T>
std::mutex ThreadPool<T>::_singletonLock;

1、调用getInstance时需要加锁,防止多个线程同时调用,实例化出多份对象。

2、双重if判断,避免不必要的锁竞争。

  • 16
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒋灵瑜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值