🔥个人主页: Milestone-里程碑
❄️个人专栏: <<力扣hot100>> <<C++>><<Linux>>
🌟心向往之行必能至
正式实现线程池之前,我们要进行引入前面的封装,及实现日志
一.日志与策略模式
什么是设计模式
IT⾏业这么⽕, 涌⼊的⼈很多. 俗话说林⼦⼤了啥⻦都有. ⼤佬和菜鸡们两极分化的越来越严重. 为了让 菜鸡们不太拖⼤佬的后腿, 于是⼤佬们针对⼀些经典的常⻅的场景, 给定了⼀些对应的解决⽅案, 这个就是 设计模式
1.1 日志的认识
计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信
息,帮助快速定位问题并⽀持程序员进⾏问题修复。它是系统维护、故障排查和安全管理的重要⼯
具。
⽇志格式以下⼏个指标是必须得有的
• 时间戳
• ⽇志等级
• ⽇志内容
以下⼏个指标是可选的
• ⽂件名⾏号
• 进程,线程相关id信息等
bash[可读性很好的时间] [⽇志等级] [进程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文件写入:使用 ofstream
ofstream 类用于向文件写入数据。要写入文件,首先需要包含头文件 <fstream> 并创建一个 ofstream 对象。使用 open() 方法打开文件,然后通过插入运算符 << 写入数据。完成写入后,调用 close() 方法关闭文件流。
bashNAME time - run programs and summarize system resource usage SYNOPSIS time [ -apqvV ] [ -f FORMAT ] [ -o FILE ] [ --append ] [ --verbose ] [ --quiet ] [ --portability ] [ --format=FORMAT ] [ --output=FILE ] [ --version ] [ --help ] COMMAND [ ARGS ]C++17 文件系统操作
C++17 引入了 std::filesystem 库,用于跨平台处理文件和目录操作,提供了路径管理、文件遍历、属性获取与修改等功能,极大简化了文件系统编程。
核心类
path:路径处理的基础类,封装字符串路径并提供解析、拼接等操作。
directory_entry:文件入口类,用于访问文件或目录的属性。
directory_iterator:目录迭代器,用于遍历目录内容。
file_status:文件状态类,用于获取文件类型、权限等信息。
示例:遍历目录与文件信息
bash#include <iostream> #include <cstdio> #include <string> #include <filesystem> //C++17 #include <sstream> #include <fstream> #include <memory> #include <ctime> #include <unistd.h> #include "mutex.hpp" namespace Logmtuble { using namespace Mutexmtuble; using namespace std; const std::string gsep = "\r\n"; class LogSratery { public: ~LogSratery() = default; virtual void SyncLog(const string &messages) = 0; }; class MonitorLogSratery : public LogSratery { public: MonitorLogSratery() {} void SyncLog(const std::string &messages) override { Lockguard lg(_mutex); cout << messages << gsep; } private: Mutex _mutex; }; const string path = "./log"; const string file = "my.long"; class FileLogSratery : public LogSratery { public: FileLogSratery(string p = path, string f = file) : _path(p), _file(f) { Lockguard lg(_mutex); if (filesystem::exists(_path)) return; try { filesystem::create_directories(_path); // 路径不'存在.就创造 } catch (const filesystem::filesystem_error &e) { std::cerr << e.what() << '\n'; } } void SyncLog(const string &messages) override { string filename = _path + (_path.back() == '/' ? "" : "/") + _file; ofstream out(filename, ios::app); // 追击写入 if (!out.is_open()) { return; } out << messages << gsep; out.close(); } ~FileLogSratery() {} private: Mutex _mutex; string _path; // 所在路径 string _file; // 写入文件 }; // 上面可供选择不同的刷新策略与写入方式 // 形成日志等级 enum class LogLeval { DEBUG, INFO, WARNING, ERROR, FATAL }; string Level2Str(LogLeval level) { switch (level) { case LogLeval::DEBUG: return "DEBUG"; case LogLeval::INFO: return "INFO"; case LogLeval::WARNING: return "WARNING"; case LogLeval::ERROR: return "ERROR"; case LogLeval::FATAL: return "FATAL"; default: return "UNKNOWN"; } } string GetTimeStamp() { time_t curr = time(nullptr); struct tm curr_tm; localtime_r(&curr, &curr_tm); char timebuffer[128]; snprintf(timebuffer, sizeof(timebuffer), "%4d-%02d-%02d %02d:%02d:%02d", curr_tm.tm_year + 1900, curr_tm.tm_mon + 1, curr_tm.tm_mday, curr_tm.tm_hour, curr_tm.tm_min, curr_tm.tm_sec); return timebuffer; } class Logger { public: Logger() { EnableMonitorLogSratery(); } void EnableMonitorLogSratery() { _fflush_strategy = make_unique<MonitorLogSratery>(); } void EnalbleFileSratery() { _fflush_strategy = make_unique<FileLogSratery>(); } // 表示未来的一条日志 class LogMessage { public: LogMessage(LogLeval &level, string &src_name, int line_number, Logger &logger) : _curr_time(GetTimeStamp()), _level(level), _pid(getpid()), _src_name(src_name), _line_number(line_number), _logger(logger) { stringstream ss; ss << "[" << _curr_time << "] " << "[" << Level2Str(_level) << "] " << "[" << _pid << "] " << "[" << _src_name << "] " << "[" << _line_number << "] " << "- "; _loginfo = ss.str(); } // LogMessage() << "hell world" << "XXXX" << 3.14 << 1234 template <class T> LogMessage &operator<<(const T &info) { stringstream ss; ss << info; _loginfo += ss.str(); return *this; } ~LogMessage() { if (_logger._fflush_strategy) { _logger._fflush_strategy->SyncLog(_loginfo); } } private: string _curr_time; LogLeval _level; pid_t _pid; string _src_name; int _line_number; string _loginfo; // 一条完整的 Logger &_logger; }; // 这里故意写成返回临时对象 LogMessage operator()(LogLeval level, string name, int line) { return LogMessage(level, name, line, *this); } ~Logger() { } private: unique_ptr<LogSratery> _fflush_strategy; }; // 使用宏,简化用户操作,获取文件名和行号 #define LOG(level) logger(level, __FILE__, __LINE__) #define Enable_File_Log_Srategy() logger.FileSratery() #define Enable_Monitor_Log_Srategy() logger.MonitorLogSratery() }线程池
bash#include <iostream> #include <vector> #include <queue> #include "Log.hpp" #include "cond.hpp" #include "Thread.hpp" using namespace Logmtuble; using namespace Condmtuble; using namespace Milestone; const int gnum = 5; template <class T> class threadpool { private: void WakeUpAllThread() { LockGuard lockguard(_mutex); if (_sleepernum) _cond.Broadcast(); LOG(LogLevel::INFO) << "唤醒所有休眠线程"; } void WakeUpOne() { _cond.Signal(); LOG(LogLevel::INFO) << "唤醒一个休眠线程"; } void join() { if(!_isrunning) return; for(auto&thread_threads) { thread.join(); } } public: threadpool(int num = gnum) : _num(num), _isrunning(false) _sleeprnum(0) { for (int i = 0; i < num; i++) { _threads.emplace_back( [this]() { HandlerTask(); }); } } void Start() { if (_isrunning) return; _isrunning = true; for (auto &thread : _threads) { thread.start(); LOG(LogLevel::INFO) << "start new thread success: " << thread.Name(); } } void HandlerTask() { char name[128]; pthread_getname_np(pthread_self(), name, sizeof(name)); while (true) { T t; { Lockguard lg(_mutex); // 1. a.队列为空 b. 线程池没有退出 while (_taskq.empty() && _isrunning) { _sleepernum++; _cond.Wait(_mutex); _sleepernum--; } // 2. 内部的线程被唤醒 if (!_isrunning && _taskq.empty()) { LOG(LogLevel::INFO) << name << " 退出了, 线程池退出&&任务队列为空"; break; } // 一定有任务 t = _taskq.front(); // 从q中获取任务, _taskq.pop(); } t(); } } bool Enqueue(const T &in) { if (_isrunning) { LockGuard lockguard(_mutex); _taskq.push(in); if (_threads.size() == _sleepernum) WakeUpOne(); return true; } return false; } ~ThreadPool() { } private: vector<Thread> _threads; int _num; // 线程池的线程个数 queue<T> _taskq; Cond _cond; Mutex _mutex; bool _isrunning; int _sleepernum; };