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

2023-11-16


需要云服务器等云产品来学习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判断,避免不必要的锁竞争。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

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

  • 除了 iptables 之外还有数据包管理实用程序吗? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我正在寻找一个 Linux 实用程序 它可以根据一组规则更改网络数据包的有效负载 理想情况下 我会使用
  • 从 Java 读取 /dev/input/js0

    我正在尝试阅读 dev input js0来自Java 但我不断得到 java io IOException Invalid argument at java io FileInputStream read0 Native Method a
  • 如何让 PHP、符号链接和 __FILE__ 很好地协同工作?

    在本地主机上 我有以下目录结构 share www trunk wp content plugins otherfolders share www portfolio wp content symlink Where symlink是一个符
  • 编译器 libstdc++ 版本与系统版本

    我试图了解 g 如何选择它链接的 libstdc 版本 以及当库的 系统 版本不同时它意味着什么 我正在使用 gcc g 4 1 2 根据ABI 指南 http gcc gnu org onlinedocs libstdc manual a
  • 干净地销毁System V共享内存段

    我在用shmget shmat and shmctl分别获取和创建共享内存段 将其附加到进程地址空间中并删除它 我想知道进程是否仍然可以使用共享内存段 即使它已被分离并要求使用删除 shmctl id IPC RMID 在一个过程中 我无法
  • 让“git pull”在拉取不同分支时要求确认

    当同时处理许多项目和分支时 我偶尔会犯一些愚蠢的错误 比如拉入错误的分支 例如在分支上master I did git pull origin dangerous code并且有一段时间没有注意到这一点 这个小错误造成了很大的混乱 当我尝试
  • 代码::块 - 警告:GDB:无法设置控制终端:不允许操作

    我已经通过官方存储库在 Ubuntu 14 04 中安装了 Code Blocks 13 12 当我编译时 一切正常 但是当我调试时 shell 中会显示以下消息 警告 GDB 无法设置控制终端 操作不正确 允许的 程序执行到断点 但当我执
  • 访问 Linux 线程(pthreads)的本地堆栈

    我目前正在实现一个使用多线程但对总内存消耗有要求的应用程序 我希望有一个主线程执行 I O 并有几个工作线程执行计算 目前 我在主堆栈上有几个可供工作人员访问的数据结构 我使用 OpenMP 进行工作分配 由于主 工作者模式不能很好地与 O
  • 如何从脚本向 sudo 提供密码?

    请注意 这是在我的本地计算机上运行的来宾虚拟机 VBox 我不担心安全性 我正在编写一个将在 Linux Ubuntu VM 上执行的脚本myuser用户 该脚本将在下面创建一个非常大的目录树 etc myapp 目前我必须手动完成所有这些
  • 在Linux伪终端中执行从一个终端发送到另一个终端的字符串

    假设我有一个终端 其中 tty 的输出是 dev pts 2 我想从另一个终端向第一个终端发送命令并执行它 使用 echo ls gt dev pts 2 仅在第一个终端中打印 ls 有没有办法执行字符串 不 终端不执行命令 它们只是数据的
  • Mono 和 WebRequest 速度 - 测试

    在 mono 4 6 2 linux 中 我注意到 wget 下载文件的速度与webclient DownloadString 所以我做了一个小测试来调查 为什么 wget 明显比 C 快 根据我自己的实验 使用 wget 下载 手动读取文
  • 如何删除树莓派的相机预览

    我在我的 raspberryPi 上安装了 SimpleCv 并安装了用于使用相机板的驱动程序 uv4l 驱动程序 现在我想使用它 当我在 simpleCV shell Camera 0 getImage save foo jpg 上键入时
  • 无法为 Python 3.4 创建工作虚拟环境

    I 安装Python 3 4 2 https docs python org 3 using unix html building python和我的 Linux Mint 17 1 中的 Virtualenv 12 0 5 然后我尝试创建
  • 测试linux下磁盘空间不足

    我有一个程序 当写入某个文件的磁盘空间不足时 该程序可能会死掉 我不确定是否是这种情况 我想运行它并查看 但我的测试服务器不会很快耗尽空间 有什么办法可以嘲笑这种行为吗 看起来没有任何方法可以在 Ubuntu 中设置文件夹 文件大小限制 并
  • 如何使用 bash 脚本关闭所有终端,在每个终端中有效地按 Ctrl+Shift+Q

    我经常打开许多终端 其中一些正在运行重要的进程 例如服务器 而另一些则没有运行任何东西并且可以关闭 如果您按 重要 则会弹出确认提示Cntrl Shift Q在其中 如下所示 我想要一个 bash 脚本 它可以关闭所有终端 但将 重要 终端
  • /proc/PID 文件格式

    我想从中检索一些流程信息 proc目录 我的问题如下 中的文件是否有标准格式 proc PID 例如 有这个proc PID status文件与Name t ProcName在第一行 我可以在其他地方用空格代替这个文件吗 t或者类似的东西
  • gnome-terminal 新选项卡,使用别名作为要执行的命令

    我已经创建了一个别名 bashrc文件如下 alias myproject cd Desktop myproject 当我重新启动终端时保存文件后 输入myproject带我到项目目录 但是当我尝试使用别名作为新的命令参数时gnome te
  • 如何阅读shell命令的源代码?

    我想阅读编写linux命令的实际源代码 我已经获得了一些使用它们的经验 现在我认为是时候与我的机器进行更深层次的交互了 我在这里找到了一些命令http directory fsf org wiki GNU http directory fs
  • 通过 SSH 将变量传递给远程脚本

    我正在通过 SSH 从本地服务器在远程服务器上运行脚本 首先使用 SCP 复制该脚本 然后在传递一些参数时调用该脚本 如下所示 scp path to script server example org another path ssh s
  • 如何使用ffmpeg重叠和合并多个音频文件?

    我正在尝试将多个音频文件合并到一个文件中 但我可以使用以下命令来连接 而不是连接 ffmpeg v debug i file1 wav i file2 wav i file3 wav filter complex 0 0 concat n

随机推荐

  • cmake入门系列总结四

    cmake入门系列总结四 版本说明 版本 作者 日期 备注 0 1 loon 2019 3 14 初稿 目录 文章目录 cmake入门系列总结四 版本说明 目录 一 添加系统自省 二 具体目录结构和演示 三 最后 一 添加系统自省 接下来让
  • Rxjava初步理解

    本质就是通过回调实现 Observable被观察对像 成员变量onSubscribe Subscriber 观察对象 订阅 Observable subscribe先调用Subscriber onStart 然后调用onSubscribe中
  • 如何怎样安装nvm

    使用宝塔的Nodejs环境体验比较差 如果要完全使用命令行 应该如何玩转呢 今天我们简要介绍下步骤 方便大家自己使用 首先 我们创建一个文件install sh 然后将下面代码复制粘贴进去 usr bin env bash this ens
  • PowerDesigner生成Excel版本的数据库文件

    File pdm2excel txt Title pdm export to excel Purpose To export the tables and columns to Excel Model Physical Data Model
  • Python - Numpy库的使用(简单易懂)

    目录 numpy多维数组 数组的创建 1 array函数创建数组对象 2 通过arange linspace函数创建等差数组对象 3 通过logspace函数创建等比数列数组 函数 zeros ones diag eye full nump
  • 区块链技术的特点

    区块链技术是一种去中心化的分布式账本技术 它有以下优势和应用场景 1 去中心化 区块链是一种去中心化的技术 因此可以在不需要任何中心机构或者第三方的情况下 实现信息交换和价值转移 这使得它可以在许多领域得到应用 2 可追溯性 区块链技术可以
  • 为什么设置不了这是一台家用计算机,图文演示win10专业版更改不了这是一台家庭计算机的详尽处理步骤...

    win10系统从发布到现在已经好多年了 各种问题也得到了解决 但是今天还是有用户说出现了win10专业版更改不了这是一台家庭计算机的问题 很多网友都没有关于win10专业版更改不了这是一台家庭计算机的问题的处理经验 如果你咨询很多人都不知道
  • 520表白季——你表白成功了吗

    晓玲 小A 你昨天不是向D君表白了吗 今天这是怎么了 怎么还一副伤心的样子 泪眼汪汪的 小A 5555 晓玲姐姐 D君他居然说我是花心大萝卜 我明明就只喜欢他一个啊 晓玲 这样啊 走 我们去找定位帮帮忙 Ctrl G调出定位对话框 gt 定
  • JS 正则表达式截取指定字符的前面后面的内容

    1 js截取两个字符串之间的内容 let str web辣鸡工程师前端 str str match web S 前端 1 alert str 辣鸡工程师 2 js截取某个字符串前面的内容 var str 内容 截取字符串 str str m
  • 如何在vue中引入百度地图

    1 进入百度地图开放平台官网 进行实名认证 https lbsyun baidu com 2 完成实名认证后 进入控制台 3 进入应用管理 gt 我的应用 gt 创建应用 4 根据应用场景更改应用类型 5 白名单填写 gt 提交 6 这样就
  • 机器人编程课必要性

    机器人编程课必要性 说起小孩的学习 想必家长们都是非常的有发言权的 很多的家长想要孩子去学习机器人编程的课程 但是有的家长对于机器人编程课必要性并不是特别的清楚 他们不知道孩子学习机器人编程有啥好处 今天我们就一起来了解一下机器人编程课必要
  • 年末盘点,2021年最值得推荐的10个提高开发效率工具,程序员必备

    程序员的日常工作中 好用的工具往往能让我们事半功倍 极大提升我们的开发效率 接下来分享下我平时工作中经常使用的一些工具 也欢迎大家在评论区给我推荐一些好用的工具软件 一起学习 一 网络抓包工具 1 Proxyman 平时自己编写的程序网络通
  • MySQL 多种查询方法

    这里写目录标题 查询 1 单表查询 1 选择表中的若干列 2 选择表中的若干元组 3 order by子句 4 聚集函数 5 group by分组 2 连接查询 1 等值与非等值连接查询 2 自身连接 3 外连接 4 多表连接 3 嵌套查询
  • CSS学习(六)

    定位 为什么需要定位 提问 以下情况使用标准流或者浮动能实现吗 某个元素可以自由的在一个盒子内移动位置 并且压住其他盒子 当我们滚动窗口的时候 盒子是固定屏幕某个位置的 以上效果 标准流或浮动都无法快速实现 此时需要定位来实现 所以 浮动可
  • vscode 检查c代码语法和补全_VScode下搭配ESLint、TSLint、stylelint的代码检查配方

    使用VScode打开项目时 避免项目路径过深 尽量做到开发a项目就打开a项目 如 dir path path a这样的路径 不要vscode打开 dir来开发 a 因为可能会导致eslint的一些提示出现不准确的现象 关键词 ESLint配
  • tensorflow4:创建一个简单的强化学习游戏

    Deep Q Network是DeepMind最早 2013年 提出来的 是深度强化学习方法 最开始AI什么也不会 通过给它提供游戏界面像素和分数 慢慢把它训练成游戏高手 这里首先给出一个基本的游戏例子 然后再给出强化学习方法 1 基本游戏
  • elementui 计数器

    输入值超过规定的最大值 比如999 输入1000时 输入后 直接提交表单 中间计数器会自动把1000转化为999 再提交 测试人员说没有出现 不能超过3位数字的提示 解决办法 把min和max去掉 再写自定义验证规则就可以了 计数器自动转化
  • JavaScript:iterable类型

    遍历Array可以采用下标循环 遍历Map和Set就无法使用下标 为了统一集合类型 ES6标准引入了新的iterable类型 Array Map和Set都属于iterable类型 具有iterable类型的集合可以通过新的for of循环来
  • 向量积(叉积)及其计算

    定义 两个向量a和b的 向量积 外积 叉积 是一个向量 记作a b 这里 并不是乘号 只是一种表示方法 与 不同 也可记做 若a b不共线 则a b的模是 a b a b sin a b a b的方向是 垂直于a和b 且a b和a b按这个
  • 【Linux】基于单例模式懒汉实现方式的线程池

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 LockGuard hpp 二 Task hpp 三