日志与线程池

这里写自定义目录标题

  • 日志
    • Log.hpp
    • 测试main.cpp
      • 结果
  • 线程池
    • 线程池的种类
    • ThreadPool.hpp
    • 测试 Task.hpp 和 main.cpp
      • Task.hpp
      • main.cpp
        • 结果
  • 线程池的单例模式
    • 实现方式
    • SignalThreadPool.hpp
      • 测试main.cpp
  • 线程安全与重入问题
  • 死锁
    • 死锁的四个必要条件

日志

在这里插入图片描述

日志需要包含的信息
• 时间(戳)
• ⽇志等级
• ⽇志内容
以下⼏个指标是可选的
• ⽂件名⾏号
• 进程,线程相关id信息等

要实现的日志的格式

[可读性很好的时间] [⽇志等级] [进程pid] [打印对应⽇志的⽂件名][⾏号] - 消息内容,⽀持可
变参数
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [17] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [18] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [20] - hello world
[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [21] - hello world
[2024-08-04 12:27:03] [WARNING] [202938] [main.cc] [23] - hello world

策略模式:

Log.hpp

#pragma once
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <memory>
#include <time.h>
#include <filesystem>
#include <fstream>
#include <sstream>
#include "Mutex.hpp"
namespace LogModule
{using namespace MutexModule;// 1.默认路径和文件名std::string default_path = "./log/";std::string default_filename = "log.txt";// 2.日志等级enum class LogLevel{DEBUG = 1,INFO,WARNING,ERROR,FATAL};//获得当前的时间std::string CurrTime(){time_t t = time(nullptr);struct tm cur_time;localtime_r(&t, &cur_time);char buff[64];snprintf(buff, sizeof(buff), "%4d-%02d-%02d %02d:%02d:%02d", cur_time.tm_year + 1900, cur_time.tm_mon + 1, cur_time.tm_mday, cur_time.tm_hour, cur_time.tm_min, cur_time.tm_sec);return buff;}//将相应的日志等级转换为字符串std::string LevelToString(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::WARNING:return "WARNING";case LogLevel::FATAL:return "FATAL";case LogLevel::INFO:return "INFO";case LogLevel::ERROR:return "ERROR";default:return "None";}}// 3.刷新策略//纯虚类class LogStrategy{public:virtual ~LogStrategy() {}virtual void SyncMessage(std::string message) {}};// 3.1显示器刷新//显示器刷新策略class ConsoleStrategy : public LogStrategy{public:ConsoleStrategy(){}~ConsoleStrategy(){}void SyncMessage(std::string message) override{// 向显示器上打印//显示器是临界资源需要加锁LockGuard lockguard(_mutex);std::cout << message << std::endl;}private:Mutex _mutex;};// 3.2文件刷新//文件刷新策略class FileStrategy : public LogStrategy{public:FileStrategy(std::string path = default_path, std::string filename = default_filename): _path(path),_filename(filename){std::string fullpath = _path + _filename;// 判断文件是否存在//文件资源也是临界资源,也需要加锁LockGuard lockguard(_mutex);if (std::filesystem::exists(fullpath)){return;}try{// 不存在就创建std::filesystem::create_directories(_path);}catch (std::filesystem::filesystem_error &e){std::cerr << e.what() << std::endl;}}~FileStrategy(){}void SyncMessage(std::string message) override{std::string fullpath =  _path+_filename;// 日志写入一定是追加std::ofstream of(fullpath, std::ios_base::app);LockGuard lockguard(_mutex);of << message << std::endl;}private:std::string _filename;//文件名std::string _path;//文件路径Mutex _mutex;};// 4.日志类class Logger{public:Logger(): _strategy(new ConsoleStrategy()){}~Logger(){}//Logger类的内部类class LogMessage{public:LogMessage(LogLevel level, int line, std::string filename, Logger &logger): _logger(logger),_curr_time(CurrTime()),_level(level),_pid(getpid()),_filename(filename),_line(line){//[2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16] - hello world_info = "[" + _curr_time + "] " +"[" + LevelToString(_level) + "] " +"[" + std::to_string(_pid) + "] " +"[" + _filename + "] " +"[" + std::to_string(_line) + "] - ";}//<<重载的模板意味着可以接受任何类型template <class T>LogMessage &operator<<(T data){std::stringstream ss;ss << data;_info += ss.str();return *this;}~LogMessage(){_logger._strategy->SyncMessage(_info);}private:Logger &_logger;		//应用logger可以访问Logger类的成员变量和方法了std::string _curr_time; // 当前时间LogLevel _level;        // 日志等级pid_t _pid;             // pidstd::string _filename;  // 文件名int _line;              // 行号std::string _info;      // 日志信息};// LogMessage operator()(LogLevel level, int line, std::string filename, Logger &logger)//Logger类重载的()LogMessage operator()(LogLevel level, int line, std::string filename){LogMessage logmessage(level, line, filename, *this);return logmessage;}//开启显示器刷新策略void EnableConsole(){_strategy = std::make_shared<ConsoleStrategy>();}//开启文件刷新策略void EnableFile(){_strategy = std::make_shared<FileStrategy>();}private:std::shared_ptr<LogStrategy> _strategy; // 刷新策略};Logger logger;#define LOG(level) logger(level, __LINE__, __FILE__)
#define ENABLE_CONSOLE() logger.EnableConsole()
#define ENABLE_FILE() logger.EnableFile();
}

测试main.cpp

#include "Log.hpp"
using namespace LogModule;
int main()
{//ENABLE_FILE();ENABLE_CONSOLE();int cnt = 10;while (cnt){LOG(LogLevel::DEBUG) << "hello world " << cnt;cnt--;}return 0;
}
  • 刷新的过程
    LOG(LogLevel::DEBUG)本质上是logger(level, LINE, FILE),返回值是LogMessage的临时对象,LogMessage类中又重载了**<<**,所以信息被整合到LogMessage的成员变量_info中,最后调用Logger类中共享指针调用刷新策略来进行_info的刷新。

结果

  • 显示器刷新策略
    在这里插入图片描述
  • 打开ENABLE_FILE(),就是文件刷新策略
    在这里插入图片描述

线程池

线程池的种类

a. 创建固定数量线程池,循环从任务队列中获取任务对象,获取到任务对象后,执⾏任务对象中的任务接口
b. 浮动线程池,其他同上
此处,我们选择固定线程个数的

ThreadPool.hpp

#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <functional>
#include <unistd.h>
#include "thread.hpp"
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Task.hpp"
namespace ThreadPollModule
{using namespace ThreadModule;using namespace LogModule;using namespace MutexModule;using namespace CondModule;int default_thread_num = 5;//默认线程数量template <class T>class ThreadPool{using task_t = T;//T是要处理的任务类型,也是用户自己定义的bool IsEmpty(){return _task_q.empty();}void HandlerTask(std::string name){while (true){// 1.获取任务// 访问临界资源需要枷锁{//此时is_running是false,任务是有限的,每个线程都处理任务,直到没有任务,这是退出线程LockGuard lockguard(_mutex);while (IsEmpty()&&_is_running){_wait_num++;_cond.Wait(_mutex);_wait_num--;}if(IsEmpty()&&!_is_running)break;}// 2. 处理任务// 是同步的可以不加锁,这样效率更高task_t t = _task_q.front();_task_q.pop();t(); // 这里规定,用户传进来的任务对象需要重载()来表示任务}LOG(LogLevel::INFO)<<"退出线程"<<name<<"...成功";}public:ThreadPool(int thread_num = default_thread_num): _thread_num(thread_num),_is_running(false){// 创建线程for (int i = 0; i < _thread_num; i++){//绑定对象,因为HandlerTask是类内成员Thread t(std::bind(&ThreadPollModule::ThreadPool<T>::HandlerTask,this,std::placeholders::_1));LOG(LogLevel::INFO) << "创建线程"<<t.Name();_threads.push_back(t);}}~ThreadPool(){}//开启线程池void Start(){if(_is_running)return;_is_running = true;for (auto &e : _threads){LOG(LogLevel::INFO) << "启动线程"<<e.Name();e.Start();}}//回收所有线程void Wait(){for (auto &e : _threads){e.Join();LOG(LogLevel::INFO) << "回收线程" << e.Name()<<"...成功";}}//关闭线程池void Stop(){if(!_is_running)return;_is_running = false;_cond.NotifyAll();}//将任务入任务队列void Equeue(const task_t &t){if(!_is_running)return;LOG(LogLevel::INFO)<<"发送一个任务";LockGuard lockguard(_mutex);_task_q.push(t);if (_wait_num > 0)_cond.Notify();}private:bool _is_running;             //线程池是否工作int _thread_num;              // 线程数量int _wait_num;                // 等待的线程数量std::vector<Thread> _threads; // 线程std::queue<task_t> _task_q;   // 任务队列Mutex _mutex;                 // 锁Cond _cond;};
}

需要注意的是停止线程池时,对线程退出线程的理解。
stop:需要的条件

  1. 线程自己退出(要唤醒所有线程)
  2. 历史的任务都被处理完了
  3. 不能再入任务

测试 Task.hpp 和 main.cpp

Task.hpp

#pragma once
#include <iostream>class Task
{
public://任务void operator()(){std::cout<<"这是一个Task,我正在处理一个任务"<<std::endl;}
private:
};

main.cpp

#include "ThreadPool.hpp"
#include "Task.hpp"
using namespace ThreadPollModule;
int main()
{Task t;ThreadPool<Task> tp;int cnt = 5;tp.Start();while(cnt){tp.Equeue(t);sleep(1);cnt--;}tp.Stop();tp.Wait();return 0;
}
结果

在这里插入图片描述
先创建,再启动,之后就是处理任务,最后退出线程和回收线程。

线程池的单例模式

  • 某些类, 只应该具有⼀个对象(实例), 就称之为单例.

实现方式

  • 饿汉模式:提前准备
template <typename T>
class Singleton {
static T data;//直接就初始化好了
public:
static T* GetInstance() {
return &data;
}
};
  • 懒汉模式:等到用的时候,再做相应的准备工作
    比如:缺页中断,一开始并没有申请相应的物理空间,只有使用时,发生中断,才会申请物理内存
template <typename T>
class Singleton {
static T* inst;//初始化为nullptr
public:
static T* GetInstance() {
if (inst == NULL) {
inst = new T();//需要使用时,再创建
}
return inst;
}
};

懒汉模式的优点就是,资源利用率高,因为只有用到的时候才会申请,而不是,现在用不到的也申请出来。

  • 不允许拷贝和赋值,构造函数设为私有

SignalThreadPool.hpp

#pragma once
#include <iostream>
#include <vector>
#include <queue>
#include <functional>
#include <unistd.h>
#include "thread.hpp"
#include "Log.hpp"
#include "Mutex.hpp"
#include "Cond.hpp"
#include "Task.hpp"
namespace ThreadPollModule
{using namespace ThreadModule;using namespace LogModule;using namespace MutexModule;using namespace CondModule;int default_thread_num = 5;template <class T>class ThreadPool{using task_t = T;public:static ThreadPool<T> *GetInstance(){if (instance == nullptr){LockGuard lockguard(mutex);if (instance == nullptr){instance = new ThreadPool<T>();}}return instance;}private:ThreadPool(const ThreadPool<T> &tp) = delete; // 禁止拷贝构造const ThreadPool<T> &operator=(const ThreadPool<T> &tp) = delete;// 构造设为私有ThreadPool(int thread_num = default_thread_num): _thread_num(thread_num),_is_running(false){// 创建线程for (int i = 0; i < _thread_num; i++){Thread t(std::bind(&ThreadPollModule::ThreadPool<T>::HandlerTask, this, std::placeholders::_1));LOG(LogLevel::INFO) << "创建线程" << t.Name();_threads.push_back(t);}}bool IsEmpty(){return _task_q.empty();}void HandlerTask(std::string name){while (true){// 1.获取任务// 访问临界资源需要枷锁{LockGuard lockguard(_mutex);while (IsEmpty() && _is_running){_wait_num++;_cond.Wait(_mutex);_wait_num--;}if (IsEmpty() && !_is_running)break;}// 2. 处理任务// 是同步的可以不加锁,这样效率更高task_t t = _task_q.front();_task_q.pop();t(); // 这里规定传进来的任务对象需要重载()来表示任务}LOG(LogLevel::INFO) << "回收线程" << name << "...成功";}public:~ThreadPool(){}void Start(){if (_is_running)return;_is_running = true;for (auto &e : _threads){LOG(LogLevel::INFO) << "启动线程" << e.Name();e.Start();}}void Wait(){for (auto &e : _threads){e.Join();LOG(LogLevel::INFO) << "等待线程" << e.Name() << "...成功";}}void Stop(){if (!_is_running)return;_is_running = false;_cond.NotifyAll();}void Equeue(const task_t &t){LOG(LogLevel::INFO) << "发送一个任务";LockGuard lockguard(_mutex);_task_q.push(t);if (_wait_num > 0)_cond.Notify();}private:bool _is_running;             // 线程池是否工作int _thread_num;              // 线程数量int _wait_num;                // 等待的线程数量std::vector<Thread> _threads; // 线程std::queue<task_t> _task_q;   // 任务队列Mutex _mutex;                 // 锁Cond _cond;static ThreadPool<T> *instance;static Mutex mutex;};template <class T>ThreadPool<T> *ThreadPool<T>::instance = nullptr;template <class T>Mutex ThreadPool<T>::mutex;}

测试main.cpp

#include "ThreadPool.hpp"
#include "Task.hpp"
using namespace ThreadPollModule;
int main()
{ThreadPool<Task>::GetInstance()->Start();int cnt = 5;while(cnt){//std::cout<<"发送一个任务"<<std::endl;Task t;ThreadPool<Task>::GetInstance()->Equeue(t);sleep(1);cnt--;}ThreadPool<Task>::GetInstance()->Stop();ThreadPool<Task>::GetInstance()->Wait();return 0;
}

线程安全与重入问题

  • 线程安全:描述的是线程
  • 重入:描述的是函数
    什么情况下需要考虑该函数是否是可重入的?
    多个线程访问时或当一个线程时,需要考虑信号可能导致的重入问题

死锁

在这里插入图片描述

死锁的四个必要条件

  1. 互斥条件
  2. 请求与保持
  3. 不剥夺
  4. 循环等待

避免死锁的方法就是破坏四个必要条件中的一个

  1. 尽量不使用锁
  2. 不保持
  3. 可以剥夺
  4. 避免循环等待

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

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

相关文章

1.1 数据结构的基本概念

1.1.1 基本概念和术语 一、数据、数据对象、数据元素和数据项的概念和关系 数据&#xff1a;是客观事物的符号表示&#xff0c;是所有能输入到计算机中并被计算机程序处理的符号的总称。 数据是计算机程序加工的原料。 数据对象&#xff1a;是具有相同性质的数据元素的集合&…

泷羽sec学习打卡-shell命令5

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于shell的那些事儿-shell5 字符串运算符逻辑运算符之布尔运算符实践是检验真理的唯一标准 字符串运算…

Elasticearch索引mapping写入、查看、修改

作者&#xff1a;京东物流 陈晓娟 一、ES Elasticsearch是一个流行的开源搜索引擎&#xff0c;它可以将大量数据快速存储和检索。Elasticsearch还提供了强大的实时分析和聚合查询功能&#xff0c;数据模式更加灵活。它不需要预先定义固定的数据结构&#xff0c;可以随时添加或修…

Mybatis Plus 增删改查方法(一、增)

先定义一个简单的测试表&#xff0c;执行脚本如下&#xff1a; create table user(id bigint primary key auto_increment,name varchar(255) not null,age int not null default 0 check (age > 0) ); 根据Spingbootmybatisplus的结构根据表自行构建结构&#xff0c;大致…

本地部署 WireGuard 无需公网 IP 实现异地组网

WireGuard 是一个高性能、极简且易于配置的开源虚拟组网协议。使用路由侠内网穿透使其相互通讯。 第一步&#xff0c;服务端&#xff08;假设为公司电脑&#xff09;和客户端&#xff08;假设为公司外的电脑&#xff09;安装部署 WireGuard 1&#xff0c;点此下载&#xff08;…

unity中添加预制体及其基本设置

unity中添加预制体及其基本设置 Unity 中使用预制体的好处使用示例代码解释 Unity 中使用预制体的好处 1. 提高代码复用性 预制体可将一个游戏对象及其所有组件、子对象和设置存储在一个资源文件中&#xff0c;然后在项目中多次使用这个资源。这大大提高了代码的复用性&#x…

给定一个整数可能为正,0,负数,统计这个数据的位数.

题目描述 给定一个整数可能为正,0,负数,统计这个数据的位数. 例如1234567输出7位; -12345678输出8位;0输出1位 代码实现 int main() { long long m; long long n; scanf("%lld",&n); mn; int count0;//位数 do { count; n/10;//舍弃个位 }while(n!0); printf(&…

Linux:文件系统inode

早期&#xff0c;存储文件的设备是磁盘&#xff08;当下的市场几乎都是SSD&#xff09;&#xff0c;但大家习惯的把它们都称为磁盘&#xff0c;磁盘是用来表示区分内存的存储设备。而在操作系统看来&#xff0c;这个存储设备的结构就是一个线性结构&#xff0c;这一点很重要。 …

C++STL之vector(超详细)

CSTL之vector 1.vector基本介绍2.vector重要接口2.1.构造函数2.2.迭代器2.3.空间2.3.1.resize2.3.2.capacity 2.4.增删查找 3.迭代器失效4.迭代器分类 &#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680;&#x1f68…

深入浅出机器学习中的梯度下降算法

大家好&#xff0c;在机器学习中&#xff0c;梯度下降算法&#xff08;Gradient Descent&#xff09;是一个重要的概念。它是一种优化算法&#xff0c;用于最小化目标函数&#xff0c;通常是损失函数。梯度下降可以帮助找到一个模型最优的参数&#xff0c;使得模型的预测更加准…

树莓派5+文心一言 -> 智能音箱

一、简介 效果&#xff1a;运行起来后&#xff0c;可以连续对话 硬件&#xff1a;树莓派5、麦克风、音箱&#xff0c;成本500-1000 软件&#xff1a;snowboy作为唤醒词、百度语音作为语音识别、brain作为指令匹配、百度文心一言作为对话模块、微软的edge-tts语音合成... 二…

Springboot——SseEmitter流式输出

文章目录 前言SseEmitter 简介测试demo注意点异常一 ResponseBodyEmitter is already set complete 前言 最近做AI类的开发&#xff0c;看到各大AI模型的输出方式都是采取的一种EventStream的方式实现。 不是通常的等接口处理完成后&#xff0c;一次性返回。 而是片段式的处理…

5G学习笔记之随机接入

目录 1. 概述 2. MSG1 2.1 选择SSB 2.2 选择Preamble Index 2.3 选择发送Preamble的时频资源 2.4 确定RA-RNTI 2.5 确定发送功率 3. MSG2 4. MSG3 5. MSG4 6. 其它 6.1 切换中的随机接入 6.2 SI请求的随机接入 6.3 通过PDCCH order重新建立同步 1. 概述 随机接入…

【Linux-多线程】重谈地址空间+内存管理方式

一、背景知识 a.重谈地址空间 我们之前已经说过&#xff0c;CPU内部见的地址&#xff0c;以及我们打印出来的地址都是虚拟地址&#xff1b;物理内存加载到CPU&#xff0c;CPU内执行进程创建内核数据结构&#xff0c;页表等&#xff0c;通过页表映射到物理磁盘上&#xff1b;也…

Spark Optimization —— Reducing Shuffle

Spark Optimization : Reducing Shuffle “Shuffling is the only thing which Nature cannot undo.” — Arthur Eddington Shuffle Shuffle Shuffle I used to see people playing cards and using the word “Shuffle” even before I knew how to play it. Shuffling in c…

Elasticsearch——Java API 操作

Elasticsearch 软件是由Java语言开发的,所以也可以通过JavaAPI的方式对 Elasticsearch服务进行访问。 创建 Maven 项目 我们在 IDEA 开发工具中创建 Maven 项目(模块也可)ES。并修改pom文件&#xff0c;增加Maven依赖关系。 #直接复制在pom文件的<dependencies></de…

量化的8位LLM训练和推理使用bitsandbytes在AMD GPUs上

Quantized 8-bit LLM training and inference using bitsandbytes on AMD GPUs — ROCm Blogs 在这篇博客文章中&#xff0c;我们将介绍bitsandbytes的8位表示方式。正如你将看到的&#xff0c;bitsandbytes的8位表示方式显著地减少了微调和推理大语言模型&#xff08;LLMs&…

自回归(Autoregressive)模型概述

自回归&#xff08;Autoregressive&#xff09;模型概述 自回归&#xff08;Autoregressive&#xff0c;简称AR&#xff09;模型是一类基于“历史数据”来预测未来数据的模型。其核心思想是模型的输出不仅依赖于当前输入&#xff0c;还依赖于先前的输出。自回归模型通常用于时…

Win11电脑亮度无法调节以及夜间模式点击没有用失效解决方法

一、问题 最近&#xff0c;突然感觉屏幕亮度十分刺眼&#xff0c;想调整为夜间模式&#xff0c;发现点了夜间模式根本没用&#xff0c;亮度也是变成了灰色。 明明前几天还能调节的&#xff0c;这实在是太难受了&#xff01; 二、原因 这是远程控制软件向日葵的问题 在向日葵…

Linux笔记---进程:进程终止

1. 进程终止概念与分类 进程终止是指一个正在运行的进程结束其执行的操作。以下是一些常见的导致进程终止的情况&#xff1a; 一、正常终止 完成任务当进程完成了它被设计要执行的任务后&#xff0c;就会正常终止。收到特定信号在操作系统中&#xff0c;进程可能会收到来自操作…