Linux - 线程池

news2025/1/22 11:40:39

线程池

什么是池?

池化技术的核心就是"提前准备并重复利用资源". 减少资源创建和销毁的成本.

那么线程池就是提前准备好一些线程, 当有任务来临时, 就可以直接交给这些线程运行, 当线程完成这些任务后, 并不会被销毁, 而是继续等待任务. 那么这些线程在程序运行过程中只需要创建一次. 相比当任务来临时创建线程, 当线程完成任务后销毁线程这种方法. 省去了很多线程创建销毁的成本.

一次性创建多个线程, 那么就需要将这些线程集中管理起来, 将这先线程集中起来并进行管理, 这样的集合就时线程池.

线程池实现

可以看到在线程池中, 我们至少需要对线程和任务进行管理

线程: 这里我们使用 vector 进行管理

任务: 这里使用 queue 对任务进行管理

首先对于传递给线程的数据先进行一步封装

class ThreadData
{
public:
    ThreadData(const std::string& name, void* arg)
    :_name(name),
    _arg(arg)
    {}

    std::string _name; // 本线程名称
    void* _arg; // 本线程所需(参数)数据
};

然后对线程也进行封装

class Thread
{
public:
    Thread(const int& num, const std::string& name, func_t func, void* arg)
    :_num(num),
    _name(name),
    _func(func),
    _data(name, arg)
    {}

    void start() // 只有调用 start 函数, 线程才会真正被创建
    {
        pthread_create(&_t, NULL, _func, (void*)&_data);
    }

    void join()
    {
        pthread_join(_t, NULL);
    }

private:
    int _num; // 第几号线程
    pthread_t _t; // 线程的 pthread_t
    std::string _name; // 线程名称
    func_t _func; // 线程要执行的函数
    ThreadData _data; // 线程所需要的参数(数据)
};

最后再是线程池 threadpool 代码.

#include<iostream>
#include<pthread.h>
#include<vector>
#include<queue>
#include<functional>
#include<unistd.h>

// 对于线程要执行的函数的重命名
typedef void*(*func_t)(void*);

class ThreadData
{
public:
    ThreadData(const std::string& name, void* arg)
    :_name(name),
    _arg(arg)
    {}

    std::string _name; // 本线程名称
    void* _arg; // 本线程所需数据
};

class Thread
{
public:
    Thread(const int& num, const std::string& name, func_t func, void* arg)
    :_num(num),
    _name(name),
    _func(func),
    _data(name, arg)
    {}

    void start()
    {
        pthread_create(&_t, NULL, _func, (void*)&_data);
    }

    void join()
    {
        pthread_join(_t, NULL);
    }

private:
    int _num; // 第几号线程
    pthread_t _t; // 线程的 pthread_t
    std::string _name; // 线程名称
    func_t _func; // 线程要执行的函数
    ThreadData _data; // 线程所需要的参数(数据)
};

template<class T>
class threadpool
{
public:
    threadpool(int num = 5)
    {
        for(int i = 0; i < num; ++i)
        {
            std::string name = "线程" + std::to_string(i);
            _threads.push_back(new Thread(i, name, route, (void*)this));
        }
    }

    void run() // 让线程池中的线程都被创建出来, 并运行起来
    {
        for(int i = 0; i < _threads.size(); ++i)
        {
            _threads[i]->start();
        }
    }

    // 将数据放入线程池中
    void push(const T& data)
    {
        pthread_mutex_lock(&_mtx);
        _tasks.push(data);
        pthread_mutex_unlock(&_mtx);
        pthread_cond_signal(&_cv);
    }

    // 析构函数
    ~threadpool()
    {
        for(int i = 0; i < _threads.size(); ++i)
        {
            _threads[i]->join();
        }
    }

private:
    // 线程所要执行的函数
    // pthread_create 函数中, 传递给线程的函数类型就是 void*(*)(void*)
    // 如果这里不使用 static 静态函数, 而使用成员函数
    // 成员函数中默认会添加一个参数(this指针), 这样的化成员函数的类型就和 pthread_create 函数要求的类型不匹配
    static void* route(void* arg)
    {
        // 从 Thread 的 start 函数中可以看到, 我们传递给函数的参数类型是 ThreadData*
        // 而 ThreadData 中的 _arg的实际类型, 通过 Thread 的构造函数和 run 函数可以看到
        // 给 ThreadData 的第二个参数实际上就 this 指针, this == threadpool<T>*
        // 所以 _arg 就是这个线程池对象的指针. 那么通过 _arg 也就能访问到
        // 线程池中的 _tasks. 
        ThreadData* td = (ThreadData*)arg;
        threadpool<T>* tp = (threadpool<T>*)td->_arg;
        T task;
        while(true)
        {
            // 获取数据
            pthread_mutex_lock(&tp->_mtx);
            while(tp->_tasks.empty() == true) // 如果没有数据了, 那么就进行等待
            {
                pthread_cond_wait(&tp->_cv, &tp->_mtx);
            }
            task = tp->_tasks.front();
            tp->_tasks.pop();
            pthread_mutex_unlock(&tp->_mtx);

            // 处理数据
            // 这里直接打印, 便于观察
            std::cout << td->_name << ": "<< task << std::endl;
        }
        return NULL;
    }

private:
    std::vector<Thread*> _threads; // 管理所有的线程
    std::queue<T> _tasks; // 管理所有的任务
    pthread_mutex_t _mtx; // 线程间互斥的访问 _tasks
    pthread_cond_t _cv; // 当 _tasks 中没有任务时, 让所有的线程进行等待, 直到被唤醒
};

int main()
{
    threadpool<int> tp;
    tp.run();
    for(int i = 0; i < 10000; ++i)
    {
        tp.push(i);
    }
    sleep(5);
    exit(0);
    return 0;
}

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

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

相关文章

【K8S系列】K8s 领域深度剖析:年度技术、工具与实战总结

引言 Kubernetes作为容器编排领域的行业标准&#xff0c;在过去一年里持续进化&#xff0c;深刻推动着云原生应用开发与部署模式的革新。本文我将深入总结在使用K8s特定技术领域的进展&#xff0c;分享在过去一年中相关技术工具及平台的使用体会&#xff0c;并展示基于K8s的技术…

C++《AVL树》

在之前的学习当中我们已经了解了二叉搜索树&#xff0c;并且我们知道二叉搜索树的查找效率是无法满足我们的要求&#xff0c;当二叉树为左或者右斜树查找的效率就很低下了&#xff0c;那么这本篇当中我们就要来学习对二叉搜索树进行优化的二叉树——AVL树。在此会先来了解AVL树…

【MySQL】存储引擎有哪些?区别是什么?

频率难度60%⭐⭐⭐⭐ 这个问题其实难度并不是很大&#xff0c;只是涉及到的相关知识比较繁杂&#xff0c;比如事务、锁机制等等&#xff0c;都和存储引擎有关系。有时还会根据场景选择不同的存储引擎。 下面笔者将会根据几个部分尽可能地讲清楚 MySQL 中的存储引擎&#xff0…

王道数据结构day1

2.1线性表的定义和基本操作 1.线性表的定义 相同数据类型的数据元素的有限序列 位序(从1开始&#xff09; 表头元素&#xff0c;表尾元素 直接钱去&#xff0c;直接后继 2.线性表的基本操作 基本操作&#xff1a;创销&#xff0c;增删改查 优化插入&#xff1a; 查找

电梯系统的UML文档07

从这个类中得到的类图&#xff0c;构划出了软件的大部分设计。 系统结构视图提供软件和整个系统结构最复杂的也是最优雅的描述。和通常的软件系统相比&#xff0c;在分布式嵌入系统中了解系统组件如何协同工作是非常重要的。毕竟&#xff0c;每个类图仅仅是一个系统的静态设计…

数据恢复常见故障(五)晶振异常导致时钟Clock信号异常引发的硬盘故障

晶振是给固态硬盘“主控”芯片工作提供时钟信号的器件。 高温、高湿、撞件等都会引起晶振不起振&#xff0c;最终导致时钟信号异常。 如图是正常情况下的晶振波形。 晶振异常时&#xff0c;输出的波形&#xff0c;不起振。 由于晶振异常&#xff0c;无法提供时钟信号&#…

16.5万煤气柜柜位计故障分析

一、事故经过&#xff1a; 2015年8月1&#xff14;日20点45分&#xff0c;16.5万立煤气柜柜顶油封溢流口有大量油液溢出&#xff0c;此时雷达柜位计在计算机上示值为63.79米&#xff0c;由于接近傍晚天色较暗&#xff0c;岗位操作员并未及时发现这一异常状况。22点45分左右&…

ARM学习(42)CortexM3/M4 MPU配置

笔者之前学习过CortexR5的MPU配置,现在学习一下CortexM3/M4 MPU配置 1、背景介绍 笔者在工作中遇到NXP MPU在访问异常地址时,就会出现总线挂死,所以需要MPU抓住异常,就需要配置MPU。具体背景情况可以参考ARM学习(41)NXP MCU总线挂死,CPU could not be halted以及无法连…

STM32 FreeRTOS 任务挂起和恢复---实验

实验目标 学会vTaskSuspend( )、vTaskResume( ) 任务挂起与恢复相关API函数使用&#xff1a; start_task:用来创建其他的三个任务。 task1&#xff1a;实现LED1每500ms闪烁一次。 task2&#xff1a;实现LED2每500ms闪烁一次。 task3&#xff1a;判断按键按下逻辑&#xff0c;KE…

七.网络模型

最小(支撑)树问题 最小部分树求解&#xff1a; 破圈法&#xff1a;任取一圈&#xff0c;去掉圈中最长边&#xff0c;直到无圈&#xff1b; 加边法&#xff1a;取图G的n个孤立点&#xff5b;v1&#xff0c;v2&#xff0c;…&#xff0c; vn }作为一个支撑图&#xff0c;从最短…

web前端1--基础

&#xff08;时隔数月我又来写笔记啦~&#xff09; 1、下载vscode 1、官网下载&#xff1a;Visual Studio Code - Code Editing. Redefined 2、步骤&#xff1a; 1、点击同意 一直下一步 勾一个创建桌面快捷方式 在一直下一步 2、在桌面新建文件夹 拖到vscode图标上 打开v…

麦田物语学习笔记:保存和加载场景中的物品

目录 基本流程 1.代码思路 2.代码实现 最终效果 补充知识点 1.序列化 2.委托 基本流程 现在在切换场景后,场景中的物品即使被拾取了,也还是会被重新加载出来,所以本篇文章的任务是在切换场景前后能保留当前场景的数据 1.代码思路 (1)为了保留处在地上的物品数据,就需要…

51c~SLAM~合集1

我自己的原文哦~ https://blog.51cto.com/whaosoft/12327374 #GSLAM 自动驾驶相关~~~ 一个通用的SLAM架构和基准 GSLAM&#xff1a;A General SLAM Framework and Benchmark 开源代码&#xff1a;https://github.com/zdzhaoyong/GSLAM SLAM技术最近取得了许多成功&am…

QT调用OpenSceneGraph

OSG和osgQt编译教程&#xff0c;实测通过 一、下载OpenSceneGraph OpenSceneGraphhttps://github.com/openscenegraph/OpenSceneGraph 二、使用CMAKE编译OpenSceneGraph 1.打开cmake&#xff0c;配置源代码目录 2. CMAKE_INSTALL_PREFIX设置为install文件夹&#xff0c;生…

Git 详细安装教程以及gitlab添加SSH密钥

目录 一、下载安装 二、gitlab添加SSH密钥 一、下载安装 &#xff08;1&#xff09;去官网下载 找到下载的安装包双击进行安装。 &#xff08;2&#xff09;使用许可声明 双击下载后的 Git-2.47.1.2-64-bit.exe&#xff0c;开始安装&#xff0c;这个界面主要展示了 GPL 第…

【优选算法】4----盛最多水的容器

开始有点上强度了铁子们&#xff0c;这道算法题也是可以说很难理解的~ 想了好久才想明白~ ---------------------------------------begin--------------------------------------- 题目解析&#xff1a; 这一道题刚看题目&#xff0c;根本不知道在讲啥&#xff0c;但看到体积…

今天也是记录小程序进展的一天(破晓时8)

嗨嗨嗨朋友们&#xff0c;今天又来记录一下小程序的进展啦&#xff01;真是太激动了&#xff0c;项目又迈出了重要的一步&#xff0c;231啦&#xff01;感觉每一步的努力都在积累&#xff0c;功能逐渐完善&#xff0c;离最终上线的目标越来越近了。大家一直支持着这个项目&…

Python保留字与标识符及常变量

1、保留字 保留字&#xff1b;严格区分大小写 不可以把保留字作为变量、函数、类、模块和其他对象的名称来使用 import keyword print(keyword.kwlist) # 输出所有的保留字 print(len(keyword.kwlist)) # 获取保留字的个数 true 真 # True 真 #属于保留字&#xff0c;会…

若依报错:无法访问com.ruoyi.common.annotation

无法访问com.ruoyi.common.annotation 若依的父工程的pom文件中设置了jdk为1.8&#xff0c;将idea的jdk也改为1.8即可。

2008-2020年各省城镇登记失业率数据

2008-2020年各省城镇登记失业率数据 1、时间&#xff1a;2008-2020年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区名称、年份、城镇登记失业率 4、范围&#xff1a;31省 5、指标说明&#xff1a;城镇登记失业率是指在一定时期内&…