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

linux信号量和日志

目录

封装条件变量

POSIX信号量

初始化信号量

销毁信号量

等待信号量

发布信号量

封装信号量

基于环形队列的⽣产消费模型

日志

封装


封装条件变量

比较简单,不做讲解。

namespace CondModule
{class Cond{public:Cond(){pthread_cond_init(&_cond,nullptr);}void Wait(Mutex& mutex){pthread_cond_wait(&_cond,mutex.Get());}void Singal(){pthread_cond_signal(&_cond);}void Broadcast(){pthread_cond_broadcast(&_cond);}~Cond(){pthread_cond_destroy(&_cond);}private:pthread_cond_t _cond;};
}

至此,我们封装了互斥锁,线程和条件变量,所以我们可以用我们自己的轮子来写前面的阻塞队列。

using namespace MutexModule;
using namespace CondModule;const int defaultcap = 5;
template <typename T>
class BlockQueue
{bool IsFull() { return _q.size() >= _cal; }bool IsEmpty() { return _q.empty(); }public:BlockQueue(int cal = defaultcap): _cal(cal),_psleep_num(0),_csleep_num(0){}void Enqueue(const T &in){// pthread_mutex_lock(&_mutex); //上锁LockGuard lockguard(_mutex);//上锁// 生产者调用while (IsFull()){_psleep_num++;// pthread_cond_wait(&_full_cond, &_mutex);//等待_full_cond.Wait(_mutex);//等待_psleep_num--;}_q.push(in);if (_csleep_num > 0)// pthread_cond_signal(&_empty_cond);//唤醒_empty_cond.Singal();}T Pop(){// 消费者调用// pthread_mutex_lock(&_mutex);//上锁LockGuard lockguard(_mutex);//上锁while (IsEmpty()){_csleep_num++;// pthread_cond_wait(&_empty_cond, &_mutex);//等待_empty_cond.Wait(_mutex);_csleep_num--;}T data = _q.front();_q.pop();if (_psleep_num > 0)// pthread_cond_signal(&_full_cond);//唤醒_full_cond.Singal();//唤醒return data;}~BlockQueue(){}
private:std::queue<T> _q;int _cal; // 大小Mutex _mutex;Cond _full_cond;Cond _empty_cond;int _csleep_num; // 消费者休眠的个数int _psleep_num; // 生产者休眠的个数
};

POSIX信号量

POSIX信号量和SystemV信号量作⽤相同,都是⽤于同步操作,达到⽆冲突的访问共享资源⽬的。但 POSIX可以⽤于线程间同步。

初始化信号量

销毁信号量

等待信号量

发布信号量

封装信号量

namespace SemModule
{const int defaultvaule=1;class Sem{public:Sem(unsigned int value=defaultvaule){sem_init(&_sem,0,value);}void P(){sem_wait(&_sem);//-- 申请信号量 原子的}void V(){sem_post(&_sem);//++ 释放信号量 原子的}~Sem(){sem_destroy(&_sem);}private:sem_t _sem;};
}

基于环形队列的⽣产消费模型

  •  形队列采⽤数组模拟,⽤模运算来模拟环状特性
  • 环形结构起始状态和结束状态都是⼀样的,不好判断为空或者为满,所以可以通过加计数器或者 标记位来判断满或者空。另外也可以预留⼀个空的位置,作为满的状态

理解:

1.当队列为空,生产者先运行。

2.当队列为满,消费者先运行。

3.消费者不得超过生产者。

4.当生产者和消费者在同一个位置时,只能为空或为满,也就是说只要不为空和满,消费者和生产者就能同时进行(并发)。

我们先看单生产单消费:

代码:

const int defaultcap = 5;
using namespace SemModule;
template <class T>
class RingQueue
{
public:RingQueue(int cap = defaultcap): _cap(cap), _rq(cap), _blank_sem(cap), _data_sem(0),_p_step(0), _c_step(0){}void Equeue(const T &in){// 生产者调用_blank_sem.P();      // 申请空位置信号量_rq[_p_step++] = in; // 生产_p_step %= _cap;     // 维持环状_data_sem.V();       // 数据信号量要++}void Pop(T *out) // 输出型参数{// 消费者调用_data_sem.P();         // 申请数据信号量*out = _rq[_c_step++]; // 将数据带出去消费_c_step %= _cap;       // 维持环状_blank_sem.V();        // 空位置信号量要++}
private:std::vector<T> _rq;int _cap; // 大小// 生产者Sem _blank_sem; // 空位置信号量int _p_step;// 消费者Sem _data_sem; // 数据信号量int _c_step;
};

我们来看多生产,多消费:

多生产多消费,我们需要用锁。

不管是生产还是消费,任意时刻都是单个线程生产或消费,所以我们要用锁。

1.

问题:生产者或消费者是先申请信号量还是先加锁?

先申请信号量再加锁,这样快!

因为:我们可以先将资源全部申请出去,然后一个一个生产或消费。

如果是先加锁再申请信号量,这样就会将资源一个一个申请然后消费!显然,第一种快!

2.

我们写好了单生产单消费,就已经维持了消费者和生产者的互斥和同步,要写多生产多消费其实就是要维持消费者和消费者之间,生产者和生产者之间的同步与互斥关系!需要俩把锁。

3.

多线程使用资源,有俩种场景:

将资源整体使用,应用mutex+2元信号量(也就是条件变量),将资源按照不同块,分批使用,应用信号量。

代码:

const int defaultcap = 5;
using namespace SemModule;
using namespace MutexModule;
template <class T>
class RingQueue
{
public:RingQueue(int cap = defaultcap): _cap(cap), _rq(cap), _blank_sem(cap), _data_sem(0),_p_step(0), _c_step(0){}void Equeue(const T &in){// 生产者调用_blank_sem.P(); // 申请空位置信号量{LockGuard lockguard(_pmutex);_rq[_p_step++] = in; // 生产_p_step %= _cap;     // 维持环状}_data_sem.V(); // 数据信号量要++}void Pop(T *out) // 输出型参数{// 消费者调用_data_sem.P(); // 申请数据信号量{LockGuard lockguard(_cmutex);*out = _rq[_c_step++]; // 将数据带出去消费_c_step %= _cap;       // 维持环状}_blank_sem.V(); // 空位置信号量要++}private:std::vector<T> _rq;int _cap; // 大小Mutex _cmutex;//消费者锁Mutex _pmutex;//生产者锁// 生产者Sem _blank_sem; // 空位置信号量int _p_step;// 消费者Sem _data_sem; // 数据信号量int _c_step;
};

日志

⽇志认识

计算机中的⽇志是记录系统和软件运⾏中发⽣事件的⽂件,主要作⽤是监控运⾏状态、记录异常信 息,帮助快速定位问题并⽀持程序员进⾏问题修复。它是系统维护、故障排查和安全管理的重要⼯ 具。

⽇志格式以下⼏个指标是必须得有的:

  • 时间戳
  • ⽇志等级
  • ⽇志内容

以下⼏个指标是可选的:

  • ⽂件名⾏号
  • 进程,线程相关id信息等

⽇志有现成的解决⽅案,如:spdlog、glog、Boost.Log、Log4cxx等等,我们依旧采⽤⾃定义⽇志的 ⽅式。

我们想要的⽇志格式如下:

封装

刷新策略

日志:

我们采用内部类方法实现:

外部类可以直接通过内部类的对象访问其成员(因为内部类的定义在外部类作用域内可见)。

内部类使用外部类成员需要对象或者指针。

最后我们可以使用宏来简化调用:

代码:

namespace LogModule
{using namespace MutexModule;const std::string gsep = "\r\n";// 刷新策略基类class LogStrategy{public:~LogStrategy() = default;virtual void SyncLog(const std::string &message) = 0;};// 显示器打印:子类class ConsoleLogStrategy : public LogStrategy{public:void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << gsep;}private:Mutex _mutex;};const std::string defaultpath = "./log";const std::string defaultfile = "my.log";// 文件打印:子类class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath,const std::string &file = defaultfile): _path(path), _file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path))return;try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;std::ofstream out(filename, std::ios::app); // 追加方式写入if (!out.is_open())return;out << message << gsep;out.close();}private:std::string _path;std::string _file;Mutex _mutex;};// 日志等级enum class LogLevel{DEBUG,INFO,WARNING,ERROR,FATAL};// 将日志等级转化成stringstd::string LevelToStr(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";default:return "UNKNOWN";}}// 获得当前时间std::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(){EnableConsoleStrategy();}void EnableConsoleStrategy(){_fflush_strategy = std::make_unique<ConsoleLogStrategy>();}void EnableFileStrategy(){_fflush_strategy = std::make_unique<FileLogStrategy>();}// 将来是一条日志class LogMessage{public:LogMessage(LogLevel &level, std::string &src_name,int line_number, Logger &logger): _curr_time(GetTimeStamp()), _level(level),_src_name(src_name), _line_number(line_number),_logger(logger), _pid(getpid()){std::stringstream ss;ss << "[" << _curr_time << "]"<< "[" << LevelToStr(_level) << "]"<< "[" << _pid << "]"<< "[" << _src_name << "]"<< "[" << _line_number << "]"<< "- ";_loginfo = ss.str();}// LogMessage()<<"  "<<11template <class T>LogMessage &operator<<(const T &info){std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if (_logger._fflush_strategy){_logger._fflush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string _loginfo; // 一条日志合并的完整信息Logger &_logger;};// 重载()LogMessage operator()(LogLevel level, std::string name, int line){return LogMessage(level, name, line, *this);}private:std::unique_ptr<LogStrategy> _fflush_strategy;};// 全局Logger logger;
// 使用宏,简化用户操作,获取文件名和行号
#define LOG(level) logger(level, __FILE__, __LINE__)
#define Enable_Console_Log_Strategy() logger.EnableConsoleStrategy()
#define Enable_File_Log_Strategy() logger.EnableFileLogStrategy()
}

我们下期见。

http://www.lryc.cn/news/614060.html

相关文章:

  • 户外广告牌识别准确率↑32%:陌讯多模态融合算法实战解析
  • 【JMeter】调试取样器的使用
  • 易美教育荣膺“腾讯年度影响力国际教育品牌”双奖加冕,见证中国国际教育力量的崛起
  • 《论文阅读》传统CoT方法和提出的CoT Prompting的区分
  • 有鹿机器人:如何用±2cm精度重塑行业标准?
  • 综合项目记录:自动化备份全网服务器数据平台
  • excel 导出
  • Linux Shell:Nano 编辑器备忘
  • 影刀 —— 练习 —— 读取Excel的AB两列组成字典
  • flink闲谈
  • 锂电池保护板测试仪:守护电池安全的核心工具|深圳鑫达能
  • 基于Vue.js和Golang构建高效在线客服系统:前端实现与后端交互详解
  • 碰一碰NFC开发写好评php语言源码
  • Track Any Anomalous Object: A Granular Video Anomaly Detection Pipeline
  • DigitalProductId解密算法php版
  • 基于 Modbus TCP 的飞升 FSH-CF计量泵多段速控制优化研究
  • 如何将视频转为GIF格式,3大视频转为GIF工具
  • 使用Python将中文语音翻译成英语音频
  • 【软考架构】计算机网络中的IP地址表示和子网划分
  • 数据结构(六):树与二叉树
  • LLM驱动的数据分析组合(HoraeDB+Polars+Snorkel AI)
  • LabVIEW数字抽取滤波
  • seo-使用nuxt定义页面标题和meta等信息
  • 如何动态执行 JS 脚本
  • 机器学习概念2
  • [linux] Linux:一条指令更新DDNS
  • 如何在本地使用 DeepSeek Janus-Pro
  • 2025 前端真实试题-阿里面试题分析
  • camera人脸识别问题之二:【FFD】太阳逆光场景,人像模式后置打开美颜和滤镜,关闭heif拍摄格式对着人脸拍照,成像口红出现位置错误
  • 富士 Instax 12 和 Instax Mini 11 有什么区别?推荐购买哪一款?