Liunx--线程池的实现--0208 09

news2025/1/12 6:09:29

1. 线程池的了解

预先申请线程。线程池维护着多个线程,等待着监督管理者分配可并发执行的任务,这避免了在处理短时间任务时创建与销毁线程的代价。

2.线程池框架

2.1 线程的封装

 首先我们需要写线程的构造函数,他的编号是什么,回调函数是什么,给回调函数的参数是什么?

然后是调用pthread_create接口 让线程运行起来,以及调用pthread_join(),等待线程。还额外提供了一个可以直接返回线程名字的接口。

这里由于传入的num是要编写入线程名字的,不在传入的args参数里面,我们进行第二次封装

封装Thread.hpp

 2.2 线程池基本结构 ThreadPool.hpp

 

  • 在线程池的构造函数中,我们需要先创建出可以构造线程的空间,并把他们放在一起(vector里)便于管理。
  • 我们还需要明确线程的具体功能是什么,哪个线程去哪个回调函数。(这里为了演示,全部调用同一函数)
  • 由于线程池本身就是预先申请的线程,所以一个线程池对象如果没有结束生命周期,里面的线程也不会停止。所以我们把pthread_join接口放在线程池的析构函数中。
  • 派发任务时,所有的线程都需要从task_queue_里拿任务,在使用时需要保证线程安全,所以还需要封装一把锁。
  • 还需要条件变量来唤醒程序

2.2.1 锁的封装

#pragma once

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

class Mutex
{
public:
    Mutex(pthread_mutex_t *mtx):pmtx_(mtx)
    {}
    void lock() 
    {
        // std::cout << "要进行加锁" << std::endl;
        pthread_mutex_lock(pmtx_);
    }
    void unlock()
    {
        // std::cout << "要进行解锁" << std::endl;
        pthread_mutex_unlock(pmtx_);
    }
    ~Mutex()
    {}
private:
    pthread_mutex_t *pmtx_;
};

// RAII风格的加锁方式
class lockGuard
{
public:
    lockGuard(pthread_mutex_t *mtx):mtx_(mtx)
    {
        mtx_.lock();
    }
    ~lockGuard()
    {
        mtx_.unlock();
    }
private:
    Mutex mtx_;
};

说明:

先在程序内定义一把锁,初始化完成后,在需要加锁的地方用初始化后的锁作为lockGuard的构造函数的参数,创建一个 lockGuard对象,对象在创建时会调用构造函数,就会上锁。出了作用域后,自动调用析构函数以解锁。


2.2.2 线程池的具体代码

#pragma once
#include <iostream>
#include <queue>
#include <vector>
#include <string>
#include <unistd.h>
#include "Thread.hpp"
#include "lockGuard.hpp"

const int g_thread_num=3;
template<class T>
class ThreadPool
{
public:
    ThreadPool(int thread_num=g_thread_num)
        :num_(thread_num)
    {
        //创造线程的空间 构造线程
        for(int i=1;i<=num_;i++)
        {
            //每个线程的编号 回调函数 输出型参数
            threads_.push_back(new Thread(i,routine,nullptr)); //nullptr后续会改 
        }
        pthread_mutex_init(&lock,nullptr);
        pthread_cond_init(&cond,nullptr);
    }
    //回调函数 相当于消费者
    static void* routine(void* args) //这里单独拎出来说
    {
        ThreadData* td=(ThreadData*)(args);
    }

    //创造线程 pthread_create
    void run()
    {
        for(auto& iter:threads_)
        {
            iter->start();
            std::cout<<iter->name()<<" 启动成功"<<std::endl; 
        }

    }
    //相当于生产者
    void pushTask(const T& task)
    { 
        lockGuard lockguard(&lock)
        task_queue_.push(task);
        pthread_cond_signal(&cond);
    }
    

    ~ThreadPool()
    {
        for(auto& iter:threads_)
        {
            iter->join();
            delete iter;
        }
        pthread_mutex_destroy(&lock);
        pthread_cond_destroy(&cond);    
    }

private:
    std::vector<Thread*> threads_;
    int num_;
    std::queue<T> task_queue_;
    pthread_mutex_t lock;//保护临界区(任务队列)的一把锁
    pthread_cond_t cond;
};

2.2.3 static void* routine

由于回调函数是类似消费者的角色,但问题是他是一个静态成员函数,无法访问类内的属性,他要如何 task_queue_.pop(); ?

解决方法,我们在线程给回调函数的参数栏中传入this指针。

即上面的threads_.push_back(new Thread(i,routine,/*nullptr*/ this ));

说明:

 由于lock  task_queue_都是成员属性,静态成员函数中无法访问,所以我们需要在线程池内多添加几个接口以便于用tp指针帮助我们完成。

为了使用lockGuard的构造和析构函数以上锁和解锁,我们弄了一个作用域。

static void* routine(void* args)
    {
        ThreadData* td=(ThreadData*)(args);
        ThreadPool<T>*tp=(ThreadPool<T>)td->args_;
        while(true)
        {
            T task;
            {
                lockGuard lockguard(tp->getMutex()/*&lock*/);
                while(tp->isEmpty()/*task_queue_.empty()*/) tp->waitCond();
                // 读取任务
                task = tp->getTask(); // 任务队列是共享的-> 将任务从共享,拿到自己的私有空间
            }
            task(td->name_);

        }

    }
class ThreadPool
{
    //...
public:
    pthread_mutex_t *getMutex()
    {
        return &lock;
    }
    bool isEmpty()
    {
        return task_queue_.empty();
    }
    void waitCond()
    {
        pthread_cond_wait(&cond, &lock);
    }
    T getTask()
    {
        T t = task_queue_.front();
        task_queue_.pop();
        return t;
    }
    //...
};

3.调用模块

3.1 任务模块

#pragma once

#include <iostream>
#include <string>
#include <functional>
typedef std::function<int(int, int)> func_t;
class Task
{
public:
    Task(){}
    Task(int x, int y, func_t func):x_(x), y_(y), func_(func)
    {}
    void operator ()(const std::string &name)
    {
        std::cout << "线程 " << name << " 处理完成, 结果是: " << x_ << "+" << y_ << "=" << func_(x_, y_) << std::endl;
        // logMessage(WARNING, "%s处理完成: %d+%d=%d | %s | %d",
        //     name.c_str(), x_, y_, func_(x_, y_), __FILE__, __LINE__);
    }
public:
    int x_;
    int y_;
    // int type;
    func_t func_;
};

3.2 主函数模块

#include "ThreadPool.hpp" 
#include "Task.hpp"
#include <ctime>
#include <cstdlib>
#include <iostream>
#include <unistd.h>
int main()
{
    srand((unsigned long)time(nullptr)^getpid());
    ThreadPool<Task>*tp=new ThreadPool<Task>();
    tp->run();
    while(true)
    {
        int x=rand()%100+1;
        usleep(7234);
        int y=rand()%30+1;
        Task t(x,y,[](int x,int y)->int
        {
            return x+y;
        });
        std::cout<<"制作任务完成: "<<x <<" + "<<y<<"=?"<<std::endl;
        //推送任务
        tp->pushTask(t);
        sleep(1);
    }
    
    return 0;
}

3.3 运行结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/343119.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NXP iMX8平台上使用imx-gpu-sdk开发

By Toradex胡珊逢Apalis iMX8 计算机模块采用了 NXP 的 i.MX 8 QuadMax 处理器。该处理器具有两个 Vivante GC7000/XSVX 单元&#xff0c;能够支持 OpenCL、OpenVX、OpenGL ES 。本文将介绍如何使用 NXP 提供的 imx-gpu-sdk 编译能够在 Apalis iMX8 运行的 OpenCL 和 OpenCV 应…

Java_Maven:2. maven 构建 SSM 工程

目录 ​​​​​​​ 1 需求 2 准备数据库 3 创建一个 maven 工程 4 知识点准备 4.1 依赖传递 4.2 依赖冲突的解决 4.2.1 依赖调解原则 4.2.2 排除依赖 4.2.3 锁定版本 5 定义 pom.xml 6 Dao 层 6.1 pojo 模型类 6.2 dao 层代码 6.3 配置文件 6.4 单元测试 7 …

软件测试面试准备——(一)Selenium(1)基础问题及自动化测试

滴滴面试&#xff1a;1. 自己负责哪部分功能&#xff1f;农餐对接系统分为了两大子系统&#xff0c;一个是个人订餐系统&#xff0c;二是餐馆、个人与农产品供应商进行农产品交易系统。我主要负责组织测试人员对该系统进行测试。我们测试分为两个阶段&#xff1a;一、功能测试阶…

Http发展历史

1 缘起 有一次&#xff0c;听到有人在议论招聘面试的人员&#xff0c; 谈及应聘人员的知识深度&#xff0c;说&#xff1a;问了一些关于Http的问题&#xff0c;如Http相关结构、网络结构等&#xff0c; 然后又说&#xff0c;问没问相关原理、来源&#xff1f; 我也是有些困惑了…

Spring Boot开发实战——echarts图标填充数据

echarts模块的导入 先看看成品吧&#xff01; 有的图标的数据用了一些计算框架不是直接查数据库所以有点慢。 ok&#xff01;&#x1f603; 上正文&#xff0c;接上节Spring boot项目开发实战——&#xff08;LayUI实现前后端数据交换与定义方法渲染数据&#xff09;讲解了一般…

百度版本gactgpt即将来临,gpt人工智能机器横空出世

百度版本gactgpt即将来临&#xff0c;gpt人工智能机器横空出世&#xff0c;“一言”为定&#xff01;百度版ChatGPT确认&#xff01;李彦宏OKR曝光&#xff0c;率先应用于收索业务 gactCBT 大获&#xff0c;当下极有可能成为人工智能的 iPhone 时刻。为了在这场人工智能竞赛中…

Redis面试知识

概述 Redis 是速度非常快的非关系型(NoSQL)内存键值数据库,可以存储键和五种不同类型的值之间的映射。 键的类型只能为字符串,值支持五种数据类型:字符串、列表、集合、散列表、有序集合。 Redis 支持很多特性,例如将内存中的数据持久化到硬盘中,使用复制来扩展读性能…

解决workman部署到Linux环境无法启动和连接的问题(结合TP6框架)

0、检查Linux是否满足workman的环境要求 curl -Ss www.workerman.net | php看要求的环境是否都是ok。 如果出现&#xff1a; workman Call to undefined function stream_socket_server()解决方案&#xff1a;去PHP的配置文件查找disabled_function&#xff0c;去掉stream_s…

SAP NetWeaver版本和SAP Kernel版本的确定

SAP NetWeaver&#xff08;SAP NW&#xff09;描述了用于“业务启用”的所有软件和服务。SAP业务套件&#xff08;如ERP中央组件&#xff08;ECC&#xff09;或供应商关系管理&#xff08;SRM&#xff09;&#xff09;包含该特定业务解决方案的软件组件。 以下是SAP NetWeaver…

el-table+selet+pagination回显触发selection-change事件,打印的数组出现数据重复问题

目录 一、应用场景 二、问题描述 三、原因分析* 四、解决办法 一、应用场景 1】vue项目 2】el-conversation组件 3】使用el-table组件 4】使用type"select"属性 5】使用selection-change事件 6】使用toggleRowSelection方法 7】混合使用el-pagination组件 二…

《论文阅读》Towards Emotional Support Dialog Systems

《论文阅读》Towards Emotional Support Dialog Systems 前言简介思路出发点相关知识区别EC、ER和ESCEmotional Support Conversation任务定义ESC框架数据集总结前言 你是否也对于理解论文存在困惑? 你是否也像我之前搜索论文解读,得到只是中文翻译的解读后感到失望? 小白…

页表与线程学习

线程 线程(TCB)是进程(PCB)的基本单位。 linux认为没有进程&#xff0c;没有线程在概念上的区分&#xff0c;只有一个叫做执行流。这句话指明了都是PCB。 Linux的线程是用进程(PCB)模拟的. 这样做的好处 不用在单独设计TCB。不用维护TCB和PCB之间的关系。不用单独编写任何调度…

Day888.MySQL是怎么保证主备一致的 -MySQL实战

MySQL是怎么保证主备一致的 Hi&#xff0c;我是阿昌&#xff0c;今天学习记录的是关于MySQL是怎么保证主备一致的内容。 MySQL 能够成为现下最流行的开源数据库&#xff0c;binlog 功不可没。 在最开始&#xff0c;MySQL 是以容易学习和方便的高可用架构&#xff0c;被开发人…

从0到1一步一步玩转openEuler--13 openEuler用户组管理

文章目录13.1 创建用户组13.1.1 groupadd命令13.1.2 用户组信息文件13.1.3 创建用户组实例13.2 修改用户组13.2.1 修改GID13.2.2 修改用户组名13.3 删除用户组13.4 将用户加入用户组或从用户组中移除13.5 切换用户组在Linux中&#xff0c;每个普通用户都有一个账户&#xff0c;…

集群使用——资源管理和租户创建

概述 OceanBase 数据库是多租户的分布式数据库&#xff0c;租户使用的资源建立在资源池上。资源池包含了资源单元&#xff0c;而资源单元则规定了具体资源的量化&#xff08;如 CPU、Memory、Disk_Size 和 IOPS 等&#xff09;。 创建租户前&#xff0c;必须规定租户使用的资源…

Java 面向对象基础

文章目录一、类和对象1. 类的定义2. 对象的使用二、对象内存图三、成员变量和局部变量四、封装1. private 关键字2. this 关键字五、构造方法六、标准类制作一、类和对象 在此之前&#xff0c;我们先了解两个概念&#xff0c;对象和类。 万物皆对象&#xff0c;客观存在的事物…

C语言基础应用(一)数据类型

一、数据类型 1、数据类型的分类 2、常量 常量是固定值&#xff0c;在程序执行期间不会改变。这些固定的值&#xff0c;又叫做字面量。 2.1 常量举例 // 整型常量 举例 /*718 十进制0213 八进制0x4b 十六进制30u 无符号整数30l 长整型30ul 无符号长整型*/ // 浮点常量…

LoadRunner安装教程

备注&#xff1a;电脑最好安装有IE浏览器或者360极速版浏览器 一、下载安装包 提前下载安装文件&#xff0c;必须下载。 链接: https://pan.baidu.com/s/1blFiMIJcoE8s3uVhAxdzdA?pwdqhpt 提取码: qhpt 包含的文件有&#xff1a; 二、安装loadrunner 注意&#xff0c;以…

虚拟机CPU占用100%问题记录

项目场景 kswapd0占用CPU过高&#xff0c;严重影响服务器及虚拟机的使用。 原因分析 kswapd0 是 Linux 系统虚拟内存管理中负责换页的进程。 系统物理内存不足时&#xff0c;kswapd0 会频繁的进行换页操作&#xff08;使用swap分区与内存换页操作交换数据&#xff09;&…

java手机短信验证,并存入redis中,验证码时效5分钟

目录 1、注册发送短信账号一个账号 2、打开虚拟机&#xff0c;将redis服务端打开 3、创建springboot工程&#xff0c;导入相关依赖 4、写yml配置 5、创建controller层&#xff0c;并创建controller类 6、创建service层&#xff0c;并创建service类 7、创建工具类&#x…