当前位置: 首页 > news >正文

【Linux】简易线程池项目

线程池是一个可以巩固一些线程相关接口 && 加强理解的一个小项目。

注意:这里的线程池使用的线程并不是Linux原生接口,而是经过封装的,具体请看线程封装,为什么不使用原生接口?

因为原生接口一旦进行pthread_create直接就去执行目标函数了,我们就不能很好的控制线程;
而封装过后的接口可以进行start,在适当的时机去执行。

这里具体说一下封装后的线程,构造时需要传入名字与将要执行的可调用对象(函数指针,Lambda,仿函数),启动时start即可。

目录

  • 目标图画展示 && 设计思想
  • 设计框架
    • main的大纲
    • ThreadPool.hpp大纲
  • 代码实现细节
  • 全部代码

目标图画展示 && 设计思想

在这里插入图片描述
每当我们有任务时直接将任务push到线程池中即可,新线程会帮助我们处理任务。

那么该如何设计?
从图中可以看出线程池本质上就是一个生产者消费者模型。
而这个模型考虑到3个点足以
其一:一个用来存放数据的数据结构
其二:参与这个模型有生产者与消费者的两个角色
其三:注意生产者与生产者,消费者与消费者,生产者与消费者之间的关系即可。

设计框架

main的大纲

我们一般都是先从主函数进行一个框架的大概设计,并逐步进行填充
当然,以下的main函数肯定不是最终版本。

int main()
{std::unique_ptr<ThreadPool> tp = std::make_unique<ThreadPool>();tp->Init();tp->Start();while (true){// 生产数据tp->Equeue(/*push数据*/);sleep(1);}tp->Stop();return 0;
}

ThreadPool.hpp大纲

将T设置为模板,其中T为可调用对象,为我们的任务
下段代码中比较详细的介绍了各个部分需要做的事情

template <class T>
class ThreadPool
{
public:void HandlerTask() {// 不断循环,直到_isrunning变为false && queue中没有任务后再退出while (true){// 在这段区域中进行对queue的读取,并进行处理数据                // 这里本质上就是属于消费者部分的临界区了// 于是需要进行加锁,并且需要处理一下生产者与消费者的互斥关系}}public:ThreadPool(){}~ThreadPool(){}void Init(){// 创建一批线程(未启动),使用vector管理起来// 注意创建时需要将 线程名与HandlerTask 都传入}void Start(){// 启动线程}void Equeue(){// 这里是生产者进行放入数据的区域// 与消费者那里类似,同样需要注意对临界区的保护与处理 生产者与消费者的关系}void Stop(){// 将线程池运行标志位置为false,注意一些细节}private:                            // 可以根据自己的需要进行增删,我这里只是一个实例std::vector<MyThread> _thds;    // 管理线程的数据结构int _cap;                       // 线程池容量std::queue<T> _q;               // 生产消费模型中的交易场所bool _isrunning;                // 线程池运行标志位int _sleep_num;                 // 睡眠线程个数pthread_mutex_t _mutex;         // 锁pthread_cond_t _cond;           // 条件变量
};

代码实现细节

我们先给出一个任务hpp,没什么好说的,没有一点细节

#include <iostream>class Task
{
public:Task(int x, int y) : _x(x), _y(y){}void operator()(){_result = _x + _y;}void Debug(){std::cout << _x << " + " << _y << " = ?" << std::endl;}void Result(){std::cout << _x << " + " << _y << " = " << _result << std::endl;}
private:int _x;int _y;int _result;
};

线程池部分完整代码

template <class T>
class ThreadPool
{
private:void Lock(){pthread_mutex_lock(&_mutex);}void Unlock(){pthread_mutex_unlock(&_mutex);}void WakeUp(){pthread_cond_signal(&_cond);}void WakeUpAll(){pthread_cond_broadcast(&_cond);}bool Empty(){return _q.empty();}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}public:void HandlerTask(const std::string &name){while (true){Lock();while (Empty() && _isrunning){_sleep_num++;Sleep();_sleep_num--;}if (Empty() && !_isrunning){Unlock();break;}// 有任务T t = _q.front();_q.pop();Unlock();// 在自己独立的栈帧中并发执行任务。t();std::cout << name << " :";t.Result();}}public:ThreadPool(int cap = defaultCap) : _cap(cap), _sleep_num(0), _isrunning(false){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}void Init(){std::function<void(const std::string &)> f = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);for (int i = 0; i < _cap; i++){std::string name = "thread-" + std::to_string(i + 1);_thds.emplace_back(name, f);}}void Start(){_isrunning = true;for (auto &thd : _thds){thd.Start();}}void Equeue(const T &in){Lock();if (_isrunning){_q.emplace(in);if (_sleep_num > 0){WakeUp();}}Unlock();}void Stop(){Lock();_isrunning = false;WakeUpAll();Unlock();}private:std::vector<MyThread> _thds;int _cap;std::queue<T> _q;int _sleep_num;bool _isrunning;pthread_mutex_t _mutex;pthread_cond_t _cond;
};

其中这里有很关键的几个点

一.、HandlerTask中关于生产者与消费者关系的维护
我们的新线程没有任务时直接去休眠即可,但是由于我们还要控制线程池(_isrunning),于是这里需要增加一些逻辑,只有queue中没有任务并且_isrunning为true时才能休眠;
同样退出时需要_isrunning为false并且没有任务才能退出

二、生产者处理数据时要在锁外进行处理,否则在锁内的话就失去了多线程意义了。

三、init中,构造vector时,我们需要HandlerTask传过去,但是由于隐含的this指针,所以利用std::bind就会很舒服

四:stop中,我们除了设为false,还要进行wakeUpAll,原因在于 可能当你的queue中没有任务并且_isrunning还为true都在条件变量下休眠了,这样的话如果stop没有wakeUpAll,那么新线程就会永远sleep。

全部代码

模拟线程的代码:

#pragma once#include <iostream>
#include <string>
#include <unistd.h>
#include <cstdio>
#include <pthread.h>
#include <functional>namespace cyc
{class MyThread{public:// typedef void (*func_t)();using func_t = std::function<void(const std::string&)>;MyThread(const std::string &name, func_t func) : _func(func), _isRunning(false), _name(name){}~MyThread(){}void Excute(){_isRunning = true;_func(_name);_isRunning = false;}static void *routine(void *arg){MyThread *self = static_cast<MyThread*>(arg);self->Excute();return nullptr;}void Start(){int n = ::pthread_create(&_tid, nullptr, routine, this);if (n != 0){perror("pthread_create fail");exit(1);}std::cout << _name << " start..." << std::endl;}void Stop(){if (_isRunning){pthread_cancel(_tid);_isRunning = false;}}void Join(){int n = pthread_join(_tid, nullptr);if (n != 0){perror("pthread_join fail");exit(1);}std::cout << _name << " Join sucess..." << std::endl;}std::string GetStatus(){if (_isRunning)return "Running";elsereturn "sleeping";}private:pthread_t _tid;func_t _func;std::string _name;bool _isRunning;};
}

任务代码:

#include <iostream>class Task
{
public:Task(int x, int y) : _x(x), _y(y){}void operator()(){_result = _x + _y;}void Debug(){std::cout << _x << " + " << _y << " = ?" << std::endl;}void Result(){std::cout << _x << " + " << _y << " = " << _result << std::endl;}
private:int _x;int _y;int _result;
};

线程池代码:

#pragma once#include "MyThread.hpp"
#include <vector>
#include <queue>
#include <string>
#include <pthread.h>using namespace cyc;const int defaultCap = 5;void test()
{while (true){std::cout << "hello world" << std::endl;}
}template <class T>
class ThreadPool
{
private:void Lock(){pthread_mutex_lock(&_mutex);}void Unlock(){pthread_mutex_unlock(&_mutex);}void WakeUp(){pthread_cond_signal(&_cond);}void WakeUpAll(){pthread_cond_broadcast(&_cond);}bool Empty(){return _q.empty();}void Sleep(){pthread_cond_wait(&_cond, &_mutex);}public:void HandlerTask(const std::string &name){while (true){Lock();while (Empty() && _isrunning){_sleep_num++;Sleep();_sleep_num--;}if (Empty() && !_isrunning){Unlock();break;}// 有任务T t = _q.front();_q.pop();Unlock();// 在自己独立的栈帧中并发执行任务。t();std::cout << name << " :";t.Result();}}public:ThreadPool(int cap = defaultCap) : _cap(cap), _sleep_num(0), _isrunning(false){pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}void Init(){std::function<void(const std::string &)> f = std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1);for (int i = 0; i < _cap; i++){std::string name = "thread-" + std::to_string(i + 1);_thds.emplace_back(name, f);}}void Start(){_isrunning = true;for (auto &thd : _thds){thd.Start();}}void Equeue(const T &in){Lock();if (_isrunning){_q.emplace(in);if (_sleep_num > 0){WakeUp();}}Unlock();}void Stop(){Lock();_isrunning = false;WakeUpAll();Unlock();}private:std::vector<MyThread> _thds;int _cap;std::queue<T> _q;int _sleep_num;bool _isrunning;pthread_mutex_t _mutex;pthread_cond_t _cond;
};

main代码:

#include <unistd.h>
#include <memory>#include "ThreadPool.hpp"
#include "Task.hpp"int main()
{ThreadPool<Task> *tp = new ThreadPool<Task>;tp->Init();tp->Start();// sleep(1);int count = 3;while (count--){// 生产数据Task t(1, 2);tp->Equeue(t);sleep(1);}tp->Stop();delete tp;return 0;
}

有问随时找博主~

相关文章:

【Linux】简易线程池项目

线程池是一个可以巩固一些线程相关接口 && 加强理解的一个小项目。 注意&#xff1a;这里的线程池使用的线程并不是Linux原生接口&#xff0c;而是经过封装的&#xff0c;具体请看线程封装&#xff0c;为什么不使用原生接口&#xff1f; 因为原生接口一旦进行pthread…...

基于vue框架的NBA球星管理系统1878g(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,球员,球员数据,榜单类型,联盟榜单,重要比赛回放,精彩时刻视频,视频专栏,本赛季赛程,十佳球,投票信息,投票结果 开题报告内容 基于Vue框架的NBA球星管理系统 开题报告 一、选题背景 随着互联网的普及和体育产业的蓬勃发展&#x…...

【docker】Dockerfile练习

1、overlay文件系统原理测试 cd /mnt mkdir A B C worker merged echo "From A">./A/a.txt echo "From A">./A/b.txt echo "From A">./A/c.txt echo "From B">./B/a.txt echo "From B">./B/d.txt echo &quo…...

数据可视化的魔法:Python Matplotlib库的奇妙之旅

标题&#xff1a;数据可视化的魔法&#xff1a;Python Matplotlib库的奇妙之旅 在数据科学和分析领域&#xff0c;数据可视化是一种将复杂数据转换为图形表示的强有力工具&#xff0c;它可以帮助我们更直观地理解数据。Python中的Matplotlib库是进行数据可视化的瑞士军刀&…...

Python数据科学的秘密武器:Pandas库的深度解析

标题&#xff1a;Python数据科学的秘密武器&#xff1a;Pandas库的深度解析 Python作为数据科学领域的宠儿&#xff0c;其强大的数据处理能力离不开Pandas库的加持。Pandas是一个开源的数据分析和操作库&#xff0c;它提供了快速、灵活和表达力强的数据结构&#xff0c;旨在使…...

云计算实训24——python基本环境搭建、变量和数据类型、数据集合、py脚本

一、python环境搭建 确保拥有阿里云镜像 查看python环境 [rootpython ~]# yum list installed | grep python 查看epel是否安装 [rootpython ~]# yum list installed | grep epel 安装epel [rootpython ~]# yum -y install epel-release.noarch 查看是否安装python3 [rootpyt…...

深入了解网络性能监控(NPM):优化网络性能的关键

目录 网络性能监控&#xff08;NPM&#xff09;是什么&#xff1f; 关键网络性能指标 案例分享&#xff1a;如何利用NPM优化网络性能 实用技巧&#xff1a;如何高效运维你的网络 结论 随着企业依赖于互联网和内部网络进行业务运营&#xff0c;网络的稳定性和性能显得尤为重…...

Vue引入使用iconfont字体图标

由于element-ui或element-plus提供的图标有时候并不能满足日常需求,所以这篇介绍一下前端引入阿里巴巴矢量图标库使用,不止是vue使用,不限于vue2、vue3,html或是其他框架也是同样的道理,只要引入都是同样可以使用的。 1. 首先进入阿里巴巴矢量图标库官网 官网:https://…...

Doc2Vec

Doc2Vec 是一种扩展自 Word2Vec 的算法&#xff0c;它不仅可以生成词向量&#xff0c;还可以生成句子或文档的向量。下面是一个使用 Doc2Vec 比较两个句子的具体过程&#xff1a; 步骤 1: 训练 Doc2Vec 模型 首先&#xff0c;你需要有一个训练好的 Doc2Vec 模型。训练过程大致…...

MES生产过程透明管理,实施掌握生产每个环节

MES&#xff08;制造执行系统&#xff09;生产过程透明管理&#xff0c;旨在通过集成多种技术手段和管理模块&#xff0c;实现对生产过程的实时监控和精准掌握&#xff0c;确保每个生产环节都能被清晰地记录和追踪。以下是对MES生产过程透明管理的详细阐述&#xff1a; 一、MES…...

Java解析压缩包,并根据指定文件夹上传文件

方法 public Multimap<String, String> getCodeBucketMultimap(HttpServletRequest request)throws IOException {MultipartHttpServletRequest multiRequest (MultipartHttpServletRequest) request;// 基于servlet获取文件流List<MultipartFile> multipartFile…...

【HTML】纯前台字符验证码

效果图&#xff1a; 大致思路&#xff1a; 1.在<canvas>画布里写出几个字符&#xff1b; 2.给字符一个随机的角度和颜色&#xff1b; 3.给字符上画出一些干扰线和干扰点。 <canvas width"100" height"30" id"canvasRef" click"…...

如何在 Vue.js 项目中动态设置页面标题

目录 方法 1:使用 Vue Router 的元信息(meta) 步骤 1: 配置路由元信息 步骤 2: 使用路由守卫设置标题 方法 2:在组件内设置标题 在组件挂载时设置标题 使用响应式数据动态更新标题 在开发 Vue.js 应用时,设置动态页面标题是常见需求,尤其当应用包含多个页面时,为每…...

Eval绕过限制参数限制

PHP Eval函数参数限制在16个字符 PHP代码 <?php$param $_REQUEST[param]; if (strlen($param) < 17 && stripos($param, eval) false && stripos($param, assert) false){eval($param);}?># 部署环境属于ubuntu系统 通过GET传参绕过 由于是…...

计算机网络408考研 2021

2021 计算机网络408考研2021年真题解析_哔哩哔哩_bilibili 1 1 11 1 1 11...

element table表格树形数据展示

element table表格树形数据展示 1、效果 2、代码 <el-table ref"pointMultipleTable" border class"table-box" :data"[damActiveObj]"row-key"id" :tree-props"{ children: children }" :expand-row-keys"expand…...

Ubuntu 安装 Snipaste

一、下载 Snipaste 下载Snipastehttps://zh.snipaste.com/ 二、在/opt 创建 Snipaste 目录&#xff0c;创建 bin 和 icon 子目录&#xff0c;将 Snipaste.AppImage 移动到 bin 目录 三、创建快捷键图标 1. 创建桌面图标&#xff0c;右键→允许运行 yammiemy-pc >/home/y…...

NET8环境WebAPI实现文件的压缩及下载

目录 1、文件下载的原理2、具体实现2.1 提前准备2.2 服务器端的实现2.3 请求端的实现 3、代码下载4、更多特性4.1 单独压缩文件4.2 解析4.2.1 整体解析4.2.2 单个文件解析 4.3 其他4.3.1 设置压缩级别4.3.2 密码保护4.3.3 进度反馈 5、参考资料 1、文件下载的原理 在实际应用环…...

Ubuntu 18 使用NVIDIA上的HDMI输出声音

前言 在未做修改之前&#xff0c;Settings -> Sound -> Output 里面只有 Digital Output(S/PDIF) - Built-in Audio 不显示HDMI的输出设备检查当前存在的音频设备 sudo lspci -v | grep -A7 -i "audio"输出&#xff1a; 从输出可以看出来是有两个设备的 00:1…...

C#模拟量线性变换小程序

1、一步步建立一个C#项目 一步步建立一个C#项目(连续读取S7-1200PLC数据)_s7协议批量读取-CSDN博客文章浏览阅读1.7k次,点赞2次,收藏4次。本文详细介绍了如何使用C#构建一个项目,通过S7net库连接并连续读取S7-1200 PLC的数据,包括创建窗体应用、配置存储位置、安装S7net库…...

别再只把DeepSeek当聊天机器人了!这5个隐藏功能,让你工作效率翻倍

解锁DeepSeek的5个高阶生产力玩法&#xff1a;从聊天工具到智能副手的蜕变 当大多数人还在用DeepSeek进行基础问答时&#xff0c;进阶用户已经把它变成了私人效率引擎。这个AI平台远不止是回答问题的工具——它能重构你的工作流、优化决策过程&#xff0c;甚至成为跨领域协作的…...

Java实战:手把手教你给JPG、PNG、GIF图片批量添加AIGC隐式水印(附完整代码)

Java实战&#xff1a;批量处理图片隐式水印的工程化解决方案 在数字内容爆炸式增长的时代&#xff0c;如何有效标识和管理AIGC生成内容成为开发者面临的新挑战。本文将深入探讨Java环境下批量处理JPG、PNG、GIF图片隐式水印的完整技术方案&#xff0c;从原理分析到实战代码&…...

OpenDrop用户画像分析:揭秘不同用户群体的文件传输习惯与使用场景

OpenDrop用户画像分析&#xff1a;揭秘不同用户群体的文件传输习惯与使用场景 【免费下载链接】opendrop An open Apple AirDrop implementation written in Python 项目地址: https://gitcode.com/gh_mirrors/op/opendrop OpenDrop是一个开源Apple AirDrop实现&#xf…...

Hogan.js Lambda功能详解:高级模板替换技术终极指南

Hogan.js Lambda功能详解&#xff1a;高级模板替换技术终极指南 【免费下载链接】hogan.js A compiler for the Mustache templating language 项目地址: https://gitcode.com/gh_mirrors/ho/hogan.js Hogan.js是一个高效的Mustache模板引擎编译器&#xff0c;它提供了强…...

GHelper:重新定义华硕设备的性能控制体验 | 从技术原理到实战应用的深度解析

GHelper&#xff1a;重新定义华硕设备的性能控制体验 | 从技术原理到实战应用的深度解析 【免费下载链接】g-helper Lightweight, open-source control tool for ASUS laptops and ROG Ally. Manage performance modes, fans, GPU, battery, and RGB lighting across Zephyrus,…...

NifSkope深度解析:从入门到精通掌握专业NIF模型编辑技术

NifSkope深度解析&#xff1a;从入门到精通掌握专业NIF模型编辑技术 【免费下载链接】nifskope A git repository for nifskope. 项目地址: https://gitcode.com/gh_mirrors/ni/nifskope NifSkope是一款专注于NetImmerse文件格式&#xff08;NIF&#xff09;的专业开源3…...

Element UI Radio组件多选换行终极解决方案(附完整代码示例)

Element UI Radio组件多选换行终极解决方案&#xff08;附完整代码示例&#xff09; 在企业级后台管理系统开发中&#xff0c;表单控件的美观性和功能性同样重要。Element UI作为Vue.js生态中广泛使用的组件库&#xff0c;其Radio组件在多选场景下的换行问题常常困扰开发者。本…...

.prettierrc 典型配置(通用版)

文章目录一、完整版标准配置&#xff08;推荐&#xff09;二、极简版配置&#xff08;新手够用&#xff09;三、常用配置项说明&#xff08;一看就懂&#xff09;四、配套使用&#xff08;必看&#xff09;五、总结.prettierrc 典型配置&#xff08;通用版&#xff09;是前端项…...

基于STM32的校园一卡通系统设计与实现

1. 项目概述1.1 项目开发背景作为一名嵌入式系统开发者&#xff0c;我最近完成了一个基于STM32的校园一卡通系统项目。这个项目的灵感来源于我在大学期间亲身经历的多卡困扰——每天要带着学生证、饭卡、图书证等一堆卡片&#xff0c;不仅容易丢失&#xff0c;使用起来也很不方…...

OpenClaw硬件要求:运行Kimi-VL-A3B-Thinking多模态模型的最佳配置

OpenClaw硬件要求&#xff1a;运行Kimi-VL-A3B-Thinking多模态模型的最佳配置 1. 为什么需要关注硬件配置&#xff1f; 去年冬天&#xff0c;我第一次尝试在MacBook Pro上部署OpenClaw对接Kimi-VL-A3B-Thinking模型时&#xff0c;经历了长达3小时的"卡顿马拉松"。每…...