【Linux】系统编程简单线程池(C++)

news2025/2/23 4:19:13

目录

【1】线程池概念

【1.1】线程池

【1.2】线程池的应用场景

【1.3】线程池的种类

【1.4】线程池示例

【2】线程池代码


【1】线程池概念

【1.1】线程池

        一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

【1.2】线程池的应用场景

  • 需要大量的线程来完成任务,且完成任务的时间比较短。 WEB服务器完成网页请求这样的任务,使用线程池技术是非常合适的。因为单个任务小,而任务数量巨大,你可以想象一个热门网站的点击次数。 但对于长时间的任务,比如一个Telnet连接请求,线程池的优点就不明显了。因为Telnet会话时间比线程的创建时间大多了。

  • 对性能要求苛刻的应用,比如要求服务器迅速响应客户请求。

  • 接受突发性的大量请求,但不至于使服务器因此产生大量线程的应用。突发性大量客户请求,在没有线程池情况下,将产生大量线程,虽然理论上大部分操作系统线程数目最大值不是问题,短时间内产生大量线程可能使内存到达极限, 出现错误.

【1.3】线程池的种类

  • 半同步/半异步模式

  • 领导者/跟随着模式

【1.4】线程池示例

  • 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执行任务对象中的任务接口。

【2】线程池代码

【Makefile文件】

# 创建变量关联关系
cc=c++
standard=-std=c++11
linkLib=-l pthread
​
# 创建编译文件依赖关系
myThreadPool:ThreadPool.cc
    $(cc) -o $@ $^ $(standard) $(linkLib)  
​
# 创建删除命令
.PHONY:clean
clean:
    rm -f myThreadPool

【Task.hpp文件】

#pragma once
#include <iostream>
#include <string>
#include <functional>
​
/* 计算任务 */
class CalTask
{
public:
    using func_t = std::function<int(int, int, char)>;
​
public:
    /* 构造函数 */
    CalTask() {}
​
    /* 构造函数 */
    CalTask(int x, int y, char op, func_t func)
        : _x(x), _y(y), _op(op), _callBalk(func)
    {
    }
​
public:
    /* 仿函数 */
    std::string operator()()
    {
        int result = _callBalk(_x, _y, _op);
​
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = %d", _x, _op, _y, result);
        return buffer;
    }
​
public:
    /* 返回打印公式 */
    std::string ToTaskString()
    {
        char buffer[64];
        snprintf(buffer, sizeof(buffer), "%d %c %d = ?", _x, _op, _y);
        return buffer;
    }
​
private:
    int _x;
    int _y;
    char _op;
    func_t _callBalk;
};
​
/* 执行计算的方法 */
int MyCalculate(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 error!" << std::endl;
            result = -1;
        }
        else
        {
            result = x % y;
        }
        break;
    }
​
    default:
        break;
    }
​
    return result;
}
​
/* 保存任务 */
class SaveTask
{
public:
    using func_t = std::function<void(const std::string &)>;
​
public:
    /* 构造函数 */
    SaveTask() {}
    /* 构造函数 */
    SaveTask(const std::string &message, func_t func)
        : _message(message), _callBalk(func)
    {
    }
​
public:
    /* 仿函数 */
    void operator()()
    {
        _callBalk(_message);
    }
​
private:
    std::string _message;
    func_t _callBalk;
};
​
/* 保存方法 */
void Save(const std::string& massage){
    std::string target = "./log.txt";
    FILE *fp = fopen(target.c_str(), "a+");
    if(fp == NULL){
        std::cerr << "fopen fail!" << std::endl;
        return;
    }
​
    fputs(massage.c_str(), fp);
    fputs("\n", fp);
    fclose(fp);
}

【Thread.hpp文件】

#pragma once
#include <iostream>
#include <functional>
#include <cstdio>
#include <cassert>
​
namespace ThreadNs
{
    /* 线程连接上下文 */
    class Thread;
    class ConnectText
    {
    public:
        /* 构造函数 */
        ConnectText() : _this(nullptr), _args(nullptr) {}
        /* 析构函数 */
        ~ConnectText() {}
​
    public:
        Thread *_this;
        void *_args;
    };
​
​
    /* 线程类封装 */
    class Thread
    {
    public:
        using func_t = std::function<void *(void *)>; // // 从定义类似函数指针类型:返回值是:void*  参数是:void*
​
    public:
        /* 构造函数 */
        Thread(const int number = 0)
        {
            // 创建线程的名称
            char buffer[64];
            snprintf(buffer, sizeof(buffer), "Thread-%d", number);
            _tName = buffer;
        }
​
        /* 析构函数 */
        ~Thread() {}
        
    public:
        /* 线程启动 */
        void Start(const func_t& func, void *args = nullptr)
        {
            // 建立关系
            _func = func;
            _args = args;
​
            // 创建线程后,启动
            ConnectText *cnt = new ConnectText();
            cnt->_this = this;
            cnt->_args = _args;
​
            int n = pthread_create(&_tid, nullptr, StartRoutine, (void *)cnt);
            assert(n == 0); (void)n;       
            // 编译debug的方式时assert是存在的,release方式assert是不存在的,到时n就是定义了,但是没有被使用的变量。
            // 在有的编译器下会有warning。
        }
​
        /* 线程等待 */
        void Join()
        {
            int n = pthread_join(_tid, nullptr);
            assert(n == 0);
            (void)n;
        }
​
    public:
        /* 获取线程名字 */
        std::string GetThreadName()
        {
            return _tName;
        }
​
    private:
        /* 线程函数 */
        static void *StartRoutine(void *args)
        { // 在类内创建线程,想让线程执行对应的方法,需要将方法设置称为static
            ConnectText *cnt = static_cast<ConnectText *>(args);
            void *exRet = cnt->_this->RoutineRun(cnt->_args);
 
            delete cnt;
            return exRet;
        }
​
        void *RoutineRun(void *args)
        {
            return _func(args);
        }
​
    private:
        pthread_t _tid;     // 线程id
        std::string _tName; // 线程名称
        func_t _func;       // 线程函数
        void *_args;        // 线程参数
    };
}

【ThreadPool.hpp文件】

#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include "Thread.hpp"
#include "LockGuard.hpp"
​
/* 引用命名空间 */
using namespace ThreadNs;
​
/* 线程上下文数据 */
template <class T>
class ThreadPool;
​
template <class T>
class ThreadData
{
public:
    ThreadData(ThreadPool<T> *tp, const std::string &n)
        : _threadPool(tp), _name(n)
    {}
​
public:
    ThreadPool<T> *_threadPool;
    std::string _name;
};
​
​
​
/* 线程池封装 */
const int g_num = 5;    // 设置线程数
template <class T>
class ThreadPool
{
public:
    /* 构造函数 */
    ThreadPool(const int &num = g_num)
        : _threadNum(num)
    {
        // 初始化线程锁
        pthread_mutex_init(&_mutex, nullptr);
        // 初始化线程条件变量
        pthread_cond_init(&_cond, nullptr);
​
        // 将每个线程地址Push到线程地址容器中
        for (int i = 0; i < _threadNum; i++)
        {
            _threads.push_back(new Thread(i + 1));
        }
    }
​
    /* 析构函数 */
    ~ThreadPool()
    {
        // 释放线程锁
        pthread_mutex_destroy(&_mutex);
        // 释放线程条件变量
        pthread_cond_destroy(&_cond);
​
        // 遍历释放
        for (auto &t : _threads)
        {
            delete t;
        }
    }
​
public:
    /* 开启线程池 */
    void Run()
    {
        // 启动线程
        for (const auto &t : _threads)
        {
            ThreadData<T> *td = new ThreadData<T>(this, t->GetThreadName());
            t->Start(HandlerTask, td);
            std::cout << t->GetThreadName() << ": Start..." << std::endl;
        }
​
        // // 阻塞式等待回收线程
        // for (const auto &t : _threads)
        // {
        //     t->Join();
        //     std::cout << t->GetThreadName() << ": Recycle..." << std::endl;
        // }
    }
​
public:
    /* 获取锁 */
    pthread_mutex_t *Mutex() { return &_mutex; }
    /* 线程加锁 */
    void Lock() { pthread_mutex_lock(&_mutex); }
    /* 线程解锁 */
    void UnLock() { pthread_mutex_unlock(&_mutex); }
    /* 线程等待 */
    void ThreadWait() { pthread_cond_wait(&_cond, &_mutex); }
    /* 判断是否有任务 */
    bool IsEmpty() { return _taskQueue.empty(); }
​
    /* 新增任务 */
    void Push(const T &in)
    {
        // 加锁->自动解锁
        LockGuard LockGuard(&_mutex);
        // 新增任务
        _taskQueue.push(in);
        // 环形线程执行
        pthread_cond_signal(&_cond);
    }
​
    /* 执行任务 */
    T Pop()
    {
        // 创建T对象
        T t;
        // 获取栈顶任务
        t = _taskQueue.front();
        _taskQueue.pop();
        // 返回任务
        return t;
    }
​
private:
    /* 线程池共享线程 */
    static void *HandlerTask(void *args)
    {
        ThreadData<T> *td = static_cast<ThreadData<T> *>(args);
​
        while (true)
        {
            // 创建任务对象
            T t;
​
            {
                // 加锁->自动解锁
                LockGuard LockGuard(td->_threadPool->Mutex());
                // 如果没有任务,等待任务
                if (td->_threadPool->IsEmpty())
                {
                    td->_threadPool->ThreadWait();
                }
                t = td->_threadPool->Pop();
                // 解锁
                td->_threadPool->UnLock();
            }
​
            // 打印信息
            std::cout << td->_name << ":承接一个任务[" << t.ToTaskString() << "],任务的执行结果是[" << t() << "]" << std::endl;
        }
        return nullptr;
    }
​
private:
    int _threadNum;                 // 线程数量
    std::vector<Thread *> _threads; // 存放线程地址的容器
    std::queue<T> _taskQueue;       // 存放线程任务的队列
    pthread_mutex_t _mutex;         // 线程锁
    pthread_cond_t _cond;           // 线程条件变量
};

【ThreadPool.cc文件】

#include <iostream>
#include <memory>
#include <unistd.h>
#include "Task.hpp"
#include "ThreadPool.hpp"
using namespace std;
​
int main() 
{
    // 智能指针管理
    unique_ptr<ThreadPool<CalTask>> tP(new ThreadPool<CalTask>());
    // 启动线程池
    tP->Run();
​
    // 手动派发任务
    int x;
    int y;
    char op;
    while (1)
    {
        std::cout << "请输入数据1# ";
        std::cin >> x;
        std::cout << "请输入数据2# ";
        std::cin >> y;
        std::cout << "请输入要进行的运算# ";
        std::cin >> op;
        CalTask t(x, y, op, MyCalculate);
        tP->Push(t);
        sleep(1);
    }
    
    return 0;
}

【代码测试结果】

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

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

相关文章

【新书推荐】用户画像:了解用户,助力企业成长 ——《用户画像:平台构建与业务实践》

文章目录 〇、引子一、什么是用户画像二、用户画像的优势三、如何实现用户画像四、用户画像应用中的问题五、总结新书推荐 —— 《用户画像&#xff1a;平台构建与业务实践》内容简介目录 〇、引子 在当今市场竞争激烈的时代&#xff0c;了解用户需求、提高用户体验已成为企业…

WebGL 雾化

目录 前言 如何实现雾化 线性雾化公式 雾化因子关系图 根据雾化因子计算片元颜色公式 示例程序&#xff08;Fog.js&#xff09; 代码详解​编辑 详解如何计算雾化因子&#xff08;clamp()&#xff09; 详解如何计算最终片元颜色&#xff08;根据雾化因子计算片元颜色…

二、搭建Java环境

搭建Java环境 搭建Java环境1.1.下载JDK1.2.在Win10下配置JDK环境 —————————————————————————————————————————————————— ———————————————————————————————————————————————…

李航老师《统计学习方法》第1章阅读笔记

1.1 统计学习 统计学习的特点 统计学习&#xff1a;计算机基于数据构建概率统计模型并运用模型对数据进行预测与分析 现在人们提及机器学习时&#xff0c;往往指统计机器学习&#xff0c;所以可以认为本书介绍的是机器学习方法 统计学习的对象 统计学习研究的对象是数据(data)…

IDEA优化import导报-删除无用的包

选择File--Settings--Editor-General-Auto Import&#xff0c;勾选上下面框起来的即可&#xff0c;这样没有用到的包就会自己动被优化掉了~

了解SQL注入的类型、原理、检测与预防方法

作者&#xff1a;Insist-- 个人主页&#xff1a;insist--个人主页 梦想从未散场&#xff0c;传奇永不落幕&#xff0c;博主会持续更新优质网络知识、Python知识、Linux知识以及各种小技巧&#xff0c;愿你我共同在CSDN进步 目录 一、了解SQL注入 1. SQL注入是什么&#xff1f…

【kohya】训练自己的LoRA模型

文章目录 序言准备环境准备图片处理图片下载kohya_ss代码修改pyvenv.cfg启动界面访问地址生成字幕准备训练的文件夹配置训练参数开始训练遇到的问题&#xff1a; 序言 在把玩stable diffusion的webUI和comfyUI后&#xff0c;思考着自己也微调一个个性化风格的checkpoint、LyCO…

2023蓝帽杯南部赛区半决赛取证复现

首先嗷&#xff0c;仅代表个人评价一下就是说赛委会在出题的时候不严谨&#xff0c;我一度怀疑我的语文阅读能力有问题&#xff0c;但是呢&#xff0c;这次的取证题目虽然不是很难&#xff0c;但是有些地方我也是依旧没有找到&#xff0c;说了这么多&#xff0c;接下来&#xf…

使用BeanCopier复制对象属性值,遇到NullPointerException?

文章目录 一、场景二、问题三、分析四、解决五、总结 一、场景 项目中&#xff0c;为了数据安全&#xff0c;由于身份证属于敏感信息&#xff0c;需要加密后返回给页面解密显示&#xff0c;但DTO中出现了一种骚操作&#xff0c;身份证的get方法&#xff0c;把身份证转成大写的…

【数据结构】C++实现哈希表

闭散列哈希表 哈希表的结构 在闭散列的哈希表中&#xff0c;哈希表每个位置除了存储所给数据之外&#xff0c;还应该存储该位置当前的状态&#xff0c;哈希表中每个位置的可能状态如下&#xff1a; EMPTY&#xff08;无数据的空位置&#xff09;。EXIST&#xff08;已存储数…

Zabbix介绍与安装

目录 一、概述 二、zabbix的主要功能 三、zabbix监控原理 四、Zabbix 监控模式 五、zabbix的架构 server-client server-proxy-client master-node-client 六、zabbix的安装 安装zabbix服务端 安装zabbix客户端 测试zabbix 1、在 Web 页面中添加 agent 主机点击左…

SystemC入门学习-第3章 数据类型

本章将详细的描述SystemC的数据类型&#xff0c;并介绍这些类型的数据可以进行哪些操作。比如值保持器&#xff08;value holder&#xff09;就是一种特殊的类型。在所有的类型中&#xff0c;最重要的是bool和sc_uint两种类型 3.1 值保持器 值保持器有三种&#xff1a; 变量…

如何管理销售团队?

本文将为大家讲解&#xff1a;如何管理销售团队&#xff1f; 销售团队的管理是企业成功的关键因素之一。一个高效、协同的销售团队可以推动企业的增长&#xff0c;增强市场竞争力。然而&#xff0c;销售团队的管理并不是一件容易的事情&#xff0c;它涉及多个方面的协调和优化…

Verilog开源项目——百兆以太网交换机(二)AES加解密模块设计

Verilog开源项目——百兆以太网交换机&#xff08;二&#xff09;AES加解密模块设计 &#x1f508;声明&#xff1a;未经作者允许&#xff0c;禁止转载 &#x1f603;博主主页&#xff1a;王_嘻嘻的CSDN主页 &#x1f511;全新原创以太网交换机项目&#xff0c;Blog内容将聚焦整…

基于微信小程序的投票系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言用户微信小程序的主要功能有&#xff1a;管理员的主要功能有&#xff1a;具体实现截图论文参考为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,C…

Vue.js路由及Node.js的入门使用---超详细

一&#xff0c;Vue路由 1.1 路由是什么 路由是用来管理应用程序中不同页面之间导航的概念。Vue Router是Vue.js官方提供的路由管理器&#xff0c;它允许我们通过定义路由规则和视图组件来配置路由 1.2 路由给我们带来的好处有哪些&#xff1f; 单页应用&#xff08;Single Pag…

Springboot整合jdbc和Mybatis

目录 整合jdbc 1. 新建项目 2. 编写yaml配置文件连接数据库 3. 测试类 使用原生的jdbcTemplate进行访问测试 使用Druid连接池 1. 添加类型 2. 初始化连接池 3. 编写config类 配置Druid数据源监视 整合Mybatis 1. 导入依赖 2. 编写mapper接口 3. 编写实体类 4. 编…

GLTF编辑器的另一个作用

1、GLB模型介绍 GLB&#xff08;GLTF Binary&#xff09;是一种用于表示三维模型和场景的文件格式。GLTF是"GL Transmission Format"的缩写&#xff0c;是一种开放的、跨平台的标准&#xff0c;旨在在各种3D图形应用程序和引擎之间进行交换和共享。 GLB文件是GLTF文件…

MySQL数据库详解 二:数据库的高级语句(高级查询语句)

文章目录 1. 克隆表 ---- 将数据表的数据记录生成到新的表中1.1 方式一&#xff1a;先创建新表&#xff0c;再导入数据1.2 方式二&#xff1a;创建的时候同时导入 2. 清空表 ---- 删除表内的所有数据2.1 delete删除2.2 truncate删除&#xff08;重新记录&#xff09;2.3 创建临…

别着急,解决不了的问题,就请交给时间吧

转眼间我走出社会已过去四年之久&#xff0c;但很多事依旧历历在目&#xff0c;就好像昨天发生的一样。 我小时候&#xff0c;因为一场医学事故患有先天性白内障&#xff0c;真的是连黑板的看不清&#xff0c;当时自己也不太懂事&#xff0c;上课对我来说就是画画以及一切能够消…