【Linux】多线程_9

文章目录

  • 九、多线程
    • 10. 线程池
  • 未完待续


九、多线程

10. 线程池

这里我没实现一些 懒汉单例模式 的线程池,并且包含 日志打印 的线程池:
Makefile

threadpool:Main.ccg++ -o $@ $^ -std=c++11 -lpthread
.PHONY:clean
clean:rm -f threadpool

Thread.hpp

#ifndef __THREAD_HPP__
#define __THREAD_HPP__#include <iostream>
#include <string>
#include <unistd.h>
#include <functional>
#include <pthread.h>namespace ThreadModule
{// 类型别名using func_t = std::function<void(std::string)>;// 线程类class Thread{public:void Excute(){_func(_threadname);}public:Thread(func_t func, std::string name = "none-name"):_func(func),_threadname(name),_stop(true){}// 执行任务static void *threadroutine(void *args){Thread *self = static_cast<Thread*>(args);self->Excute();return nullptr;}// 启动线程bool Start(){int n = pthread_create(&_tid, nullptr, threadroutine, this);if(!n){_stop = false;return true;}else{return false;}}// 停止线程void Detach(){if(!_stop){pthread_detach(_tid);}}// 等待线程结束void Join(){if(!_stop){pthread_join(_tid, nullptr);}}std::string name(){return _threadname;}void Stop(){_stop = true;}~Thread(){}private:pthread_t _tid;std::string _threadname;func_t _func;bool _stop;};
}#endif

LockGuard.hpp

#ifndef __LOCK_GUARD_HPP__
#define __LOCK_GUARD_HPP__#include <iostream>
#include <pthread.h>class LockGuard
{
public:// 构造函数加锁LockGuard(pthread_mutex_t *mutex):_mutex(mutex){pthread_mutex_lock(_mutex);}// 析构函数解锁~LockGuard(){pthread_mutex_unlock(_mutex);}
private:pthread_mutex_t *_mutex;
};#endif

Log.hpp

#pragma once#include <iostream>
#include <fstream>
#include <cstdio>
#include <string>
#include <ctime>
#include <cstdarg>
#include <sys/types.h>
#include <unistd.h>
#include <pthread.h>
#include "LockGuard.hpp"// 宏定义,用于定义日志格式
#define LOG(level, format, ...) do{LogMessage(__FILE__, __LINE__, gIsSave, level, format, ##__VA_ARGS__);}while (0)
// 将日志输入到文件
#define EnableFile() do{gIsSave = true;}while (0)
// 将日志输出到显示器
#define EnableScreen() do{gIsSave = false;}while (0)bool gIsSave = false;
// 日志文件名
const std::string logname = "log.txt";// 枚举日志级别
enum Level
{DEBUG = 0,INFO,WARNING,ERROR,FATAL
};// 保存日志到文件
void SaveFile(const std::string &filename, const std::string &message)
{std::ofstream out(filename, std::ios::app);if (!out.is_open()){return;}out << message;out.close();
}// 日志级别转字符串
std::string LevelToString(int level)
{switch (level){case DEBUG:return "Debug";case INFO:return "Info";case WARNING:return "Warning";case ERROR:return "Error";case FATAL:return "Fatal";default:return "Unknown";}
}// 获取当前时间字符串
std::string GetTimeString()
{time_t curr_time = time(nullptr);struct tm *format_time = localtime(&curr_time);if (format_time == nullptr)return "None";char time_buffer[1024];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",format_time->tm_year + 1900,format_time->tm_mon + 1,format_time->tm_mday,format_time->tm_hour,format_time->tm_min,format_time->tm_sec);return time_buffer;
}// 日志锁,同一时刻只能写一个日志
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;// 日志信息
void LogMessage(std::string filename, int line, bool issave, int level, const char *format, ...)
{// 日志级别std::string levelstr = LevelToString(level);// 时间std::string timestr = GetTimeString();// 进程idpid_t selfid = getpid();// 日志内容char buffer[1024];va_list arg;va_start(arg, format);vsnprintf(buffer, sizeof(buffer), format, arg);va_end(arg);// 日志格式化std::string message = "[" + timestr + "]" + "[" + levelstr + "]" +"[" + std::to_string(selfid) + "]" +"[" + filename + "]" + "[" + std::to_string(line) + "] " + buffer + "\n";LockGuard lockguard(&lock);// 输出日志if (!issave){std::cout << message;}else{SaveFile(logname, message);}
}

Task.hpp

#pragma once#include <iostream>
#include <string>
#include <functional>class Task
{
public:Task(){}Task(int a, int b):_a(a),_b(b),_result(0){}// 执行加法功能void Excute(){_result = _a + _b;}// 结果std::string ResultToString(){return std::to_string(_a) + "+" + std::to_string(_b) + "=" + std::to_string(_result);}// 问题std::string DebugToString(){return std::to_string(_a) + "+" + std::to_string(_b) + "= ?";}// 重载()运算符void operator()(){Excute();}
private:int _a;int _b;int _result;
};

ThreadPool.hpp

#pragma once#include <iostream>
#include <vector>
#include <queue>
#include <pthread.h>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"using namespace ThreadModule;// 线程池默认线程数
const static int gdefaultthreadnum = 10;template <typename T>
class ThreadPool
{
private:// 线程互斥锁void LockQueue(){pthread_mutex_lock(&_mutex);}// 线程互斥解锁void UnlockQueue(){pthread_mutex_unlock(&_mutex);}// 线程等待void ThreadSleep(){pthread_cond_wait(&_cond, &_mutex);}// 线程唤醒void ThreadWakeup(){pthread_cond_signal(&_cond);}// 唤醒全部线程void ThreadWakeupAll(){pthread_cond_broadcast(&_cond);}// 私有构造函数ThreadPool(int threadnum = gdefaultthreadnum):_threadnum(threadnum),_waitnum(0),_isrunning(false){// 初始化锁pthread_mutex_init(&_mutex, nullptr);pthread_cond_init(&_cond, nullptr);// 日志LOG(INFO, "ThreadPool Construct()");}// 初始化线程池void InitThreadPool(){// 创建一批线程for (int num = 0; num < _threadnum; num++){std::string name = "thread-" + std::to_string(num + 1);_threads.emplace_back(std::bind(&ThreadPool::HandlerTask, this, std::placeholders::_1), name);// 日志LOG(INFO, "init thread %s done", name.c_str());}_isrunning = true;}// 启动线程池void Start(){for (auto &thread : _threads){thread.Start();}}// 任务处理函数void HandlerTask(std::string name) // 类的成员方法,也可以成为另一个类的回调方法,方便我们继续类级别的互相调用!{// 日志LOG(INFO, "%s is running...", name.c_str());while (true){// 加锁LockQueue();while (_task_queue.empty() && _isrunning){_waitnum++;ThreadSleep();_waitnum--;}// 退出情况if (_task_queue.empty() && !_isrunning){UnlockQueue();break;}// 取出任务T t = _task_queue.front();_task_queue.pop();UnlockQueue();// 日志LOG(DEBUG, "%s get a task", name.c_str());// 执行任务t();// 日志LOG(DEBUG, "%s handler a task, result is: %s", name.c_str(), t.ResultToString().c_str());}}// 禁用拷贝构造和赋值操作ThreadPool<T> &operator=(const ThreadPool<T> &) = delete;ThreadPool(const ThreadPool<T> &) = delete;
public:static ThreadPool<T> *GetInstance(){// 首次使用时,创建线程池单例if (nullptr == _instance){// 对于多线程创建单例时加锁,保证线程安全LockGuard lockguard(&_lock);if (nullptr == _instance){// 创建线程池实例_instance = new ThreadPool<T>();_instance->InitThreadPool();_instance->Start();LOG(DEBUG, "创建线程池单例");return _instance;}}// 已经创建过线程池单例,直接返回LOG(DEBUG, "获取线程池单例");return _instance;}// 停止线程池void Stop(){LockQueue();_isrunning = false;ThreadWakeupAll();UnlockQueue();}// 等待线程池退出void Wait(){for (auto &thread : _threads){thread.Join();LOG(INFO, "%s is quit...", thread.name().c_str());}}// 向线程池中添加任务bool Enqueue(const T &t){bool ret = false;LockQueue();if (_isrunning){_task_queue.push(t);if (_waitnum > 0){ThreadWakeup();}LOG(DEBUG, "enqueue task success");ret = true;}UnlockQueue();return ret;}// 析构自动释放锁资源~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}
private:// 线程池中线程个数int _threadnum;// 线程std::vector<Thread> _threads;// 任务队列std::queue<T> _task_queue;// 互斥锁pthread_mutex_t _mutex;// 条件变量pthread_cond_t _cond;// 等待线程数int _waitnum;// 线程池是否运行bool _isrunning;// 线程池单例static ThreadPool<T> *_instance;// 全局锁static pthread_mutex_t _lock;
};// 初始化静态变量
template <typename T>
ThreadPool<T> *ThreadPool<T>::_instance = nullptr;// 全局锁
template <typename T>
pthread_mutex_t ThreadPool<T>::_lock = PTHREAD_MUTEX_INITIALIZER;

Main.cc

#include "ThreadPool.hpp"
#include "Task.hpp"
#include "Log.hpp"
#include <iostream>
#include <string>
#include <memory>
#include <ctime>int main()
{// 日志LOG(DEBUG, "程序已经加载");sleep(2);// 创建线程池单例ThreadPool<Task>::GetInstance();sleep(2);// 获取单例ThreadPool<Task>::GetInstance();sleep(2);ThreadPool<Task>::GetInstance();sleep(2);ThreadPool<Task>::GetInstance();sleep(2);// 等待线程结束ThreadPool<Task>::GetInstance()->Wait();sleep(2);return 0;
}

结果演示:
在这里插入图片描述


未完待续

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

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

相关文章

element ui中el-form-item的属性rules的用法

目录 el-form-item的属性rules的用法 栗子 总结 实践应用 一、 定义静态的校验规则 二、定义动态的校验规则 el-form-item的属性rules的用法 在学习element ui 的Form表单组件时&#xff0c;学到el-form-item也有rules属性&#xff0c;但是对应这个属性如何使用&#x…

Databend 开源周报第 153 期

Databend 是一款现代云数仓。专为弹性和高效设计&#xff0c;为您的大规模分析需求保驾护航。自由且开源。即刻体验云服务&#xff1a;https://app.databend.cn 。 Whats On In Databend 探索 Databend 本周新进展&#xff0c;遇到更贴近你心意的 Databend。 支持必须更改密码…

Adobe Photoshop 2024 25.9.1 Win/Mac PS2024最新中文学习版

Adobe Photoshop 2024&#xff0c;简称PS&#xff0c;目前最强的图片处理合成软件,PS提供了广泛的工具和功能&#xff0c;包括画笔、铅笔、颜色替换、混合器画笔等绘画工具&#xff0c;以及裁剪、透视变形、智能修复画笔等编辑工具。用户可以使用这些工具进行图片编辑、合成、校…

旅游数据可视化:免费工具让复杂数据变得简单易懂

随着旅游业的蓬勃发展&#xff0c;海量的数据如同繁星点点&#xff0c;记录着每一位旅者的足迹与偏好。然而&#xff0c;如何将这些复杂的数据转化为直观、易懂的信息&#xff0c;为旅游企业精准决策、为消费者提供更加个性化的服务&#xff0c;成为了行业内外共同关注的焦点。…

vue2+antd实现表格合并;excel效果

效果图 一、html <template><div><a-table :columns"columns" :dataSource"dataSource" rowKey"id" :pagination"false" bordered><template slot"content1" slot-scope"text">{{text}}…

JuiceFS缓存特性

缓存 对于一个由对象存储和数据库组合驱动的文件系统&#xff0c;缓存是本地客户端与远端服务之间高效交互的重要纽带。读写的数据可以提前或者异步载入缓存&#xff0c;再由客户端在后台与远端服务交互执行异步上传或预取数据。相比直接与远端服务交互&#xff0c;采用缓存技…

图片如何去水印,PS 图片去水印的几种常见方法

在数字图像的世界里&#xff0c;水印常常被用来标识版权或防止未经授权的使用&#xff0c;但有时它们却成为了美观的障碍。无论是出于个人偏好还是专业需求&#xff0c;去除图片上的水印已经成为一项常见的任务。 Adobe Photoshop 作为行业标准的图像编辑软件&#xff0c;提供…

热门软件缺陷管理工具2024:专业评测与建议

国内外主流的10款软件缺陷管理工具软件对比&#xff1a;PingCode、Worktile、禅道、Tapd、Teambition、Tower、JIRA、Bugzilla、MantisBT、Trac。 在软件开发过程中&#xff0c;管理缺陷和漏洞常常成为一项挑战&#xff0c;尤其是在项目规模庞大时。选择一个高效的软件缺陷管理…

C语言------指针讲解(2)

目录 一、数组名的理解 二、使用指针访问数组 三、一维数组传参的本质 四、冒泡排序 五、二级指针 六、指针数组 七、指针数组模拟二维数组 一、数组名的理解 通过学习&#xff0c;我们知道&#xff1a;数组名和数组首元素的地址打印出来的结果一模一样&#xff0c;数组…

深度刨析程序中的指针

前面我们已经学习过了指针的一下性质&#xff1a; 指针就是个变量&#xff0c;用来存放地址&#xff0c;地址唯一标识的一块内存空间指针的大小是固定的4/8个字节&#xff08;32位平台/64位平台&#xff09;指针是有类型&#xff0c;指针的类型决定了指针的加减整数的步长&…

SQL 中的 EXISTS 子句:探究其用途与应用

目录 EXISTS 子句简介语法 EXISTS 与 NOT EXISTSEXISTS 子句的工作原理实际应用场景场景一&#xff1a;筛选存在关联数据的记录场景二&#xff1a;优化查询性能 EXISTS 与其他 SQL 结构的比较EXISTS vs. JOINEXISTS vs. IN 多重 EXISTS 条件在 UPDATE 语句中使用 EXISTS常见问题…

Maven学习—如何在IDEA中配置Maven?又如何创建Maven工程?(详细攻略)

目录 前言 1.在IDEA中配置Maven 2.创建Maven项目 &#xff08;1&#xff09;Maven&#xff1a;创建普通Maven工程 &#xff08;2&#xff09;Maven Archetype&#xff1a;创建Maven模板工程 前言 本篇博客将详细的介绍在IDEA中如何配置Maven&#xff0c;以及如何创建一个Ma…

分布式 I/O 系统 BL200 Modbus TCP 耦合器

BL200 耦合器是一个数据采集和控制系统&#xff0c;基于强大的 32 位微处理器设计&#xff0c;采用 Linux 操作系统&#xff0c;支持 Modbus 协议&#xff0c;可以快速接入现场 PLC、SCADA 以及 ERP 系统&#xff0c; 内置逻辑控制、边缘计算应用&#xff0c;适用于 IIoT 和工业…

园区道路车辆智能管控视频解决方案,打造安全畅通的园区交通环境

一、背景需求分析 随着企业园区的快速发展和扩张&#xff0c;道路车辆管理成为了保障园区秩序、提升运营效率及确保员工安全的重要任务。针对这一需求&#xff0c;旭帆科技TSINGSEE青犀提出了一种企业园区道路车辆管控的解决方案&#xff0c;通过整合视频监控、智能识别等技术…

Qt6 OpenCV4视频监控系统项目源码解析——附源码及编译运行步骤

很多刚毕业&#xff0c;或者想着转行到C Qt方向的小伙伴&#xff0c;平时可能拿不出比较像样的项目。这里你可要好好收藏啦。自己拿回去好好改改&#xff0c;就可以成为自己的项目经历了。祝各位找工作顺利呀。 好了。废话不多说。 这个项目架构采用的是MVC架构&#xff0c;结…

Porfinet从转DeviceNet从总线协议转换网关

产品功能 1. 远创智控YC-DNT-PN型是Porfinet从转Devicenet从工业级Porfinet 网关。‌这种网关设备允许将Porfinet网络中的设备连接到Devicenet网络中&#xff0c;‌从而实现不同工业通信协议之间的互操作性。‌这些网关设备通常具有两个以太网接口&#xff0c;‌分别用于连接Po…

STM32智能楼宇照明系统教程

目录 引言环境准备智能楼宇照明系统基础代码实现&#xff1a;实现智能楼宇照明系统 4.1 数据采集模块 4.2 数据处理与控制模块 4.3 通信与网络系统实现 4.4 用户界面与数据可视化应用场景&#xff1a;楼宇照明管理与优化问题解决方案与优化收尾与总结 1. 引言 智能楼宇照明系…

【verilog语法】$clog2(IEEE Std 1364-2005)

一、前言 在进行Verilog设计时&#xff0c;有时需要根据内存大小来计算地址的位宽&#xff0c;或者根据变量的值来确定变量的位宽&#xff0c;这时可以自己编写相关的可综合函数&#xff0c;或者verilog-2005起开始支持的$clog2系统函数。 二、自己编写的function function i…

代码规范性

代码规范性 命名规范代码格式注释代码结构异常处理代码复用代码审查空格的用法代码示例 命名规范 ​ 1、变量和函数名&#xff1a;使用驼峰命名法&#xff08;camelCase&#xff09;&#xff0c;如userName、getUserInfo。 ​ 2、常量&#xff1a;使用全大写字母&#xff0c;…

VSCode安装以及安装你需要的插件的保姆级教程

文章目录 前言vscode的简介安装vscode下载安装包 安装中文插件安装C/C插件总结 前言 Visual Studio Code&#xff08;VSCode&#xff09;是一款免费、开源且强大的代码编辑器&#xff0c;它具有易于使用的界面和丰富的功能&#xff0c;包括高级代码编辑、导航、调试等功能。VS…