【Linux】写个日志和再谈线程池

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:折纸花满衣
🏠个人专栏:信号量和线程池

在这里插入图片描述


目录

  • 👉🏻日志代码
    • Log.cpp
    • Main.cc
  • 👉🏻线程池代码
    • LockGuard.hpp(自定义互斥锁,进行加锁和解锁)
    • Thread.hpp
    • Task.hpp(安排线程任务)
    • ThreadPool.hpp
  • 👉🏻读写锁和自旋锁
    • c++读写锁实现伪代码
    • c++自旋锁实现伪代码

👉🏻日志代码

Log.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#pragma once#include <iostream>
#include <fstream>
#include <string>
#include <cstdarg>
#include <ctime>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>using namespace std;
enum
{Debug = 0,Info,Warning,Error,Fatal
};enum
{Screen = 10,//向显示器OneFile,ClassFile
};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";}
}const int defaultstyle = Screen;
const std::string default_filename = "log.";
const std::string logdir = "log";class Log
{
public:Log() : style(defaultstyle), filename(default_filename){mkdir(logdir.c_str(), 0775);}void Enable(int sty) //{style = sty;}std::string TimeStampExLocalTime(){time_t currtime = time(nullptr);struct tm* curr = localtime(&currtime);char time_buffer[128];snprintf(time_buffer, sizeof(time_buffer), "%d-%d-%d %d:%d:%d",curr->tm_year + 1900, curr->tm_mon + 1, curr->tm_mday,curr->tm_hour, curr->tm_min, curr->tm_sec);return time_buffer;}void WriteLogToOneFile(const std::string& logname, const std::string& message){umask(0);int fd = open(logname.c_str(), O_CREAT | O_WRONLY | O_APPEND, 0666);if (fd < 0) return;write(fd, message.c_str(), message.size());close(fd);// std::ofstream out(logname);// if (!out.is_open())//     return;// out.write(message.c_str(), message.size());// out.close();}void WriteLogToClassFile(const std::string& levelstr, const std::string& message){std::string logname = logdir;logname += "/";logname += filename;logname += levelstr;WriteLogToOneFile(logname, message);}void WriteLog(const std::string& levelstr, const std::string& message){switch (style){case Screen:std::cout << message;break;case OneFile:WriteLogToClassFile("all", message);break;case ClassFile:WriteLogToClassFile(levelstr, message);break;default:break;}}void LogMessage(int level, const char* format, ...) // 类C的一个日志接口{char leftbuffer[1024];std::string levelstr = LevelToString(level);std::string currtime = TimeStampExLocalTime();std::string idstr = std::to_string(getpid());char rightbuffer[1024];va_list args; // char *, void *va_start(args, format);// args 指向了可变参数部分vsnprintf(rightbuffer, sizeof(rightbuffer), format, args);va_end(args); // args = nullptr;snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%s][%s] ",levelstr.c_str(), currtime.c_str(), idstr.c_str());std::string loginfo = leftbuffer;loginfo += rightbuffer;WriteLog(levelstr, loginfo);}// void operator()(int level, const char *format, ...)// {//     LogMessage(int level, const char *format, ...)// }~Log() {}private:int style;std::string filename;
};Log lg;class Conf
{
public:Conf(){lg.Enable(Screen);}~Conf(){}
};Conf conf;

Main.cc

#include <iostream>
#include "Log.hpp"int main()
{Log lg;lg.Enable(OneFile);lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Debug, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Info, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Warning, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Error, "this is a log message: %d, %lf\n", 123, 3.14);lg.LogMessage(Fatal, "this is a log message: %d, %lf\n", 123, 3.14);return 0;
}

上述我们是将内容写进一个文件夹里lg.Enable(OneFile);
我们看结果:
在这里插入图片描述

👉🏻线程池代码

LockGuard.hpp(自定义互斥锁,进行加锁和解锁)

#pragma once#include <pthread.h>// 不定义锁,默认认为外部会给我们传入锁对象
class Mutex
{
public:Mutex(pthread_mutex_t *lock):_lock(lock){}void Lock(){pthread_mutex_lock(_lock);}void Unlock(){pthread_mutex_unlock(_lock);}~Mutex(){}private:pthread_mutex_t *_lock;
};class LockGuard
{
public:LockGuard(pthread_mutex_t *lock): _mutex(lock){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}
private:Mutex _mutex;
};

Thread.hpp

#pragma once#include <iostream>
#include <string>
#include <functional>
#include <pthread.h>// 设计方的视角
//typedef std::function<void()> func_t;
template<class T>
using func_t = std::function<void(T&)>;template<class T>
class Thread
{
public:Thread(const std::string &threadname, func_t<T> func, T &data):_tid(0), _threadname(threadname), _isrunning(false), _func(func), _data(data){}static void *ThreadRoutine(void *args) // 类内方法,{// (void)args; // 仅仅是为了防止编译器有告警Thread *ts = static_cast<Thread *>(args);ts->_func(ts->_data);return nullptr;}bool Start(){int n = pthread_create(&_tid, nullptr, ThreadRoutine, this/*?*/);if(n == 0) {_isrunning = true;return true;}else return false;}bool Join(){if(!_isrunning) return true;int n = pthread_join(_tid, nullptr);if(n == 0){_isrunning = false;return true;}return false;}std::string ThreadName(){return _threadname;}bool IsRunning(){return _isrunning;}~Thread(){}
private:pthread_t _tid;std::string _threadname;bool _isrunning;func_t<T> _func;T _data;
};

Task.hpp(安排线程任务)

#pragma once
#include <iostream>
#include <string>
#include <unistd.h>const int defaultvalue = 0;enum
{ok = 0,div_zero,mod_zero,unknow
};const std::string opers = "+-*/%";class Task
{
public:Task(){}Task(int x, int y, char op): data_x(x), data_y(y), oper(op), result(defaultvalue), code(ok){}void Run(){switch (oper){case '+':result = data_x + data_y;break;case '-':result = data_x - data_y;break;case '*':result = data_x * data_y;break;case '/':{if (data_y == 0)code = div_zero;elseresult = data_x / data_y;}break;case '%':{if (data_y == 0)code = mod_zero;elseresult = data_x % data_y;}break;default:code = unknow;break;}}void operator()(){Run();}std::string PrintTask(){std::string s;s = std::to_string(data_x);s += oper;s += std::to_string(data_y);s += "=?";return s;}std::string PrintResult(){std::string s;s = std::to_string(data_x);s += oper;s += std::to_string(data_y);s += "=";s += std::to_string(result);s += " [";s += std::to_string(code);s += "]";return s;}~Task(){}private:int data_x;int data_y;char oper; // + - * / %int result;int code; // 结果码,0: 结果可信 !0: 结果不可信,1,2,3,4
};

ThreadPool.hpp

#pragma once#include <iostream>
#include <queue>
#include <vector>
#include <pthread.h>
#include <functional>
#include "Log.hpp"
#include "Thread.hpp"
#include "LockGuard.hpp"static const int defaultnum = 5;class ThreadData
{
public:ThreadData(const std::string &name) : threadname(name){}~ThreadData(){}public:std::string threadname;
};template <class T>
class ThreadPool
{
private:ThreadPool(int thread_num = defaultnum) : _thread_num(thread_num){pthread_mutex_init(&_mutex, nullptr);//互斥锁初始化pthread_cond_init(&_cond, nullptr);//条件变量初始化// 构建指定个数的线程for (int i = 0; i < _thread_num; i++){// 待优化std::string threadname = "thread-";threadname += std::to_string(i + 1);ThreadData td(threadname);// Thread<ThreadData> t(threadname,//                      std::bind(&ThreadPool<T>::ThreadRun, this, std::placeholders::_1), td);// _threads.push_back(t);_threads.emplace_back(threadname,std::bind(&ThreadPool<T>::ThreadRun, this,std::placeholders::_1),td);//emplace_back函数直接在容器的末尾就地构造一个新的元素(直接构造),而不是先创建一个临时对象lg.LogMessage(Info, "%s is created...\n", threadname.c_str());//打印日志}}ThreadPool(const ThreadPool<T> &tp) = delete;const ThreadPool<T> &operator=(const ThreadPool<T>) = delete;public:// 有线程安全问题的(单例模式-懒汉模式):在懒汉模式下,单例实例在第一次使用时才被创建//线程不安全是因为会有多个线程进行创建实例static ThreadPool<T> *GetInstance(){if (instance == nullptr){LockGuard lockguard(&sig_lock);//为了规避线程不安全情况,要进行加锁操作if (instance == nullptr){lg.LogMessage(Info, "创建单例成功...\n");instance = new ThreadPool<T>();}}return instance;}bool Start(){// 启动for (auto &thread : _threads){thread.Start();lg.LogMessage(Info, "%s is running ...\n", thread.ThreadName().c_str());}return true;}void ThreadWait(const ThreadData &td)//线程等待{lg.LogMessage(Debug, "no task, %s is sleeping...\n", td.threadname.c_str());pthread_cond_wait(&_cond, &_mutex);}void ThreadWakeup()//唤醒线程{pthread_cond_signal(&_cond);}void checkSelf(){// 1. _task_num > _task_num_high_water && _thread_num < _thread_num_high_water// 创建更多的线程,并且更新_thread_num// 2. _task_num == _task_num_low_water && _thread_num >= _thread_num_high_water// 把自己退出了,并且更新_thread_num}void ThreadRun(ThreadData &td)//线程执行{while (true){// checkSelf()checkSelf();// 取任务T t;{LockGuard lockguard(&_mutex);while (_q.empty()){ThreadWait(td);//执行队列如果为空,则让当前线程进行等待lg.LogMessage(Debug, "thread %s is wakeup\n", td.threadname.c_str());//若执行到这一步,说明线程以及被唤醒}t = _q.front();_q.pop();}// 处理任务t();lg.LogMessage(Debug, "%s handler task %s done, result is : %s\n",td.threadname, t.PrintTask().c_str(), t.PrintResult().c_str());}}void Push(T &in){lg.LogMessage(Debug, "other thread push a task, task is : %s\n", in.PrintTask().c_str());LockGuard lockguard(&_mutex);_q.push(in);ThreadWakeup();}~ThreadPool(){pthread_mutex_destroy(&_mutex);pthread_cond_destroy(&_cond);}// for debugvoid Wait(){for (auto &thread : _threads){thread.Join();}}private:std::queue<T> _q;std::vector<Thread<ThreadData>> _threads;int _thread_num;pthread_mutex_t _mutex;pthread_cond_t _cond;static ThreadPool<T> *instance;static pthread_mutex_t sig_lock;// 扩展1:// int _thread_num;// int _task_num;// int _thread_num_low_water;  // 3// int _thread_num_high_water; // 10// int _task_num_low_water;    // 0// int _task_num_high_water;   // 30// 扩展2: 多进程+多线程// int number{1};
};template <class T>
ThreadPool<T> *ThreadPool<T>::instance = nullptr;
template <class T>
pthread_mutex_t ThreadPool<T>::sig_lock = PTHREAD_MUTEX_INITIALIZER;

👉🏻读写锁和自旋锁

当多个线程尝试同时访问共享资源时,为了保证数据的一致性,需要使用锁来实现同步。读加锁和自旋锁都是常见的锁机制,用于控制对共享资源的访问。

  1. 读加锁(Read Lock):

    • 读加锁允许多个线程同时对共享资源进行读取操作,但不允许写操作。
    • 当一个线程获取了读锁后,其他线程可以继续获取读锁,但不能获取写锁,直到所有的读锁都被释放。
    • 读锁之间不会互斥,可以同时存在多个读锁
    • 读锁适用于读操作频繁,写操作较少的场景,可以提高并发读取效率。
  2. 自旋锁(Spin Lock):

    • 自旋锁是一种忙等待的锁机制,当一个线程尝试获取锁时,如果锁已经被其他线程占用,该线程会循环检测锁是否被释放,而不是进入睡眠状态
    • 自旋锁适用于对共享资源的访问时间非常短的情况,避免了线程频繁切换和上下文切换的开销
    • 自旋锁在多核处理器上效果更好,因为在等待锁的过程中可以利用处理器时间进行自旋操作,提高效率。

总的来说,读加锁适合读操作频繁的场景,而自旋锁适合对共享资源访问时间短、线程并发量不高的场景。选择合适的锁机制可以提高程序的并发性能和效率。

c++读写锁实现伪代码

读写锁(Read-Write Lock)是一种多线程同步机制,允许多个线程同时对共享资源进行读取操作,但在进行写操作时需要互斥排他。它的设计目的是在读操作频繁、写操作较少的场景下提高并发性能。

读写锁通常包括两种状态:读模式和写模式。

  • 读模式:多个线程可以同时获取读锁,以便并发地读取共享资源。
  • 写模式:当一个线程获取写锁时,其他线程无法同时获取读锁或写锁,确保写操作的互斥性。

与互斥锁不同,读写锁允许多个线程同时持有读锁,这样可以提高并发性能。只有当有线程持有写锁时,其他线程才不能获取读锁或写锁。

读写锁的使用场景通常是在数据的读操作远远多于写操作的情况下,通过允许并发读取来提高性能。然而,如果写操作频繁,读写锁可能会失去优势,因为写操作会阻塞所有的读操作。

在C++中,可以使用 std::shared_mutex 来实现读写锁的功能,通过 lock_shared() 获取读锁,通过 lock() 获取写锁,实现对共享资源的安全访问。

#include <iostream>
#include <thread>
#include <mutex>std::mutex mtx;
int sharedData = 0;void writer() {mtx.lock(); // 加锁sharedData++; // 写操作std::cout << "Writer updated data to: " << sharedData << std::endl;mtx.unlock(); // 解锁
}int main() {std::thread writerThread1(writer);std::thread writerThread2(writer);writerThread1.join();writerThread2.join();return 0;
}

c++自旋锁实现伪代码

C++11 提供的原子操作来实现自旋锁

#include <atomic>class SpinLock {
public:void lock() {while (locked.test_and_set(std::memory_order_acquire)) {// 自旋等待锁释放}}void unlock() {locked.clear(std::memory_order_release);}private:std::atomic_flag locked = ATOMIC_FLAG_INIT;
};

如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长

在这里插入图片描述
在这里插入图片描述

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

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

相关文章

Spring Boot集成zxing实现生成二维码功能

1.二维码介绍 二维码QR Code(Quick Response Code) 由Denso公司于1994年9月研制的一种矩阵二维码符号&#xff0c;它具有一维条码及其它二维条码所具有的信息容量大、可靠性高、可表示汉字及图象多种文字信息、保密防伪性强等优点。 ZXing 一个支持在图像中解码和生成条形码(如…

【Web】NKCTF 2024 个人wp(部分)

目录 my first cms 全世界最简单的CTF attack_tacooooo 属实太菜了&#xff0c;3/4 my first cms 一眼搜版本2.2.19 CVE -CVE-2024-27622 GitHub - capture0x/CMSMadeSimple 访问/admin/login.php 爆出弱口令&#xff0c;后台登录 admin Admin123 Extensions > User D…

鸿蒙实战开发-使用关系型数据库实现对账单的增、删、改、查

介绍 本Codelab以记账为例&#xff0c;使用关系型数据库的相关接口实现了对账单的增、删、改、查操作。实现效果如图所示&#xff1a; 相关概念 关系型数据库&#xff1a;基于关系模型来管理数据的数据库&#xff0c;提供了增、删、改、查等接口&#xff0c;也可运行输入的SQ…

Mac电脑高清媒体播放器:Movist Pro for mac下载

Movist Pro for mac是一款专为Mac操作系统设计的高清媒体播放器&#xff0c;支持多种常见的媒体格式&#xff0c;包括MKV、AVI、MP4等&#xff0c;能够流畅播放高清视频和音频文件。Movist Pro具有强大的解码能力和优化的渲染引擎&#xff0c;让您享受到更清晰、更流畅的观影体…

Matlab|【免费】基于数据驱动的模型预测控制电力系统机组组合优化

目录 1 主要内容 2 部分代码 3 程序结果 4 下载链接 1 主要内容 该程序复现文章《Feature-Driven Economic Improvement for Network-Constrained Unit Commitment: A Closed-Loop Predict-and-Optimize Framework》&#xff0c;程序主要做的是一个基于数据驱动的电力系统机…

51单片机学习笔记——LED闪烁和流水灯

任务分析 首先要知道LED闪烁主要是怎么工作的&#xff0c;闪烁亮灭自然是一下为高一下为低&#xff0c;亮灭的频率则需要延时来进行控制。 上节已经知道了如何点亮那延时如何做呢首先先编写主框架 这样是否可以通过循环将LED灯一直循环闪烁。 以为while一直在循环所以其实是可…

[Java基础揉碎]final关键字

目录 介绍 在某些情况下&#xff0c;程序员可能有以下需求&#xff0c;就会使用到final final注意事项和讨论细节 1) final修饰的属性又叫常量&#xff0c;一般用XX_XX_XX来命名 2) final修饰的属性在定义时&#xff0c;必须赋初值&#xff0c;并且以后不能再修改&#…

Spring Cloud五:Spring Cloud与持续集成/持续部署(CI/CD)

Spring Cloud一&#xff1a;Spring Cloud 简介 Spring Cloud二&#xff1a;核心组件解析 Spring Cloud三&#xff1a;API网关深入探索与实战应用 Spring Cloud四&#xff1a;微服务治理与安全 文章目录 一、Spring Cloud在CI/CD中的角色1. 服务注册与发现&#xff1a;自动化管理…

Java安全 反序列化(4) CC1链-LazyMap版

Java安全 反序列化(4) CC1链-LazyMap版 实验环境:存在漏洞的版本 commons-collections3.1-3.2.1 jdk 8u71之后已修复不可利⽤ 文章目录 Java安全 反序列化(4) CC1链-LazyMap版一.跟踪挖掘CC1_LazyMap原理二.完整CC1_Lazy版Poc 接着上一篇文章我们通过ChainedTransFormer实现任意…

client-go中ListAndWatch机制,informer源码详解

文章首发地址&#xff1a; 学一下 (suxueit.com)https://suxueit.com/article_detail/s9UMb44BWZdDRfKqFv22 先上一张&#xff0c;不知道是那个大佬画的图 简单描述一下流程 client-go封装部分 以pod为例 、先List所有的Pod资源&#xff0c;然后通过已经获取的pod资源的最大版…

SQLiteC/C++接口详细介绍sqlite3_stmt类(八)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;七&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;九&#xff09; 27、sqlite3_column_int 函数 sqlite3_column_int 用于返…

0.2W超精密金属膜电阻器-陶瓷封装-塑封

高精度欧姆值&#xff0c;温度系数低 长期稳定性&#xff0c;无感设计 UPSC 欧姆范围&#xff1a;40 Ω 至 5 MΩ UPR 欧姆范围&#xff1a;10 Ω 至 5 MΩ 温度系数&#xff1a;2 ppm/C 至 25 ppm/C 符合 RoHS 规范 由EE1/10(RE55)成品高精密金属膜电阻器选配组装而成&am…

java数据结构与算法刷题-----LeetCode75. 颜色分类

java数据结构与算法刷题目录&#xff08;剑指Offer、LeetCode、ACM&#xff09;-----主目录-----持续更新(进不去说明我没写完)&#xff1a;https://blog.csdn.net/grd_java/article/details/123063846 文章目录 1. 双指针两次遍历2. 三指针 1. 双指针两次遍历 解题思路&#…

以行动激发消费活力,加多宝引领高品质消费浪潮

2024年“315”期间&#xff0c;加多宝携手全国多地市场监督管理局、消费者协会等单位&#xff0c;围绕今年“激发消费活力”主题&#xff0c;积极配合各地相关政府部门开展系列宣传活动&#xff0c;以实际行动呼吁切实保护消费者合法权益&#xff0c;共建诚信消费环境&#xff…

Python综合实战案例-数据清洗分析

写在前面&#xff1a; 本次是根据前文讲解的爬虫、数据清洗、分析进行的一个纵隔讲解案例&#xff0c;也是对自己这段时间python爬虫、数据分析方向的一个总结。 本例设计一个豆瓣读书数据⽂件&#xff0c;book.xlsx⽂件保存的是爬取豆瓣⽹站得到的图书数据&#xff0c;共 6067…

在fstab文件中配置UUID方式自动挂载数据盘、swap、目录(**)

linux如何挂在硬盘&#xff0c;自动挂载和手动挂载&#xff08;详细说明&#xff09;https://gitcode.csdn.net/65eedcea1a836825ed7a06f4.html 解决linux重启后磁盘挂载失效的问题 https://blog.csdn.net/sugarbliss/article/details/107033034 linux /etc/fstab 文件详细说…

Android Audio相关

AudioManager AudioService的Bp端&#xff0c;调用AudioManager>AudioService&#xff08;代码实现&#xff09; AudioService 继承自IAudioService.Stub&#xff0c;为Bn端 AudioSystem AudioService功能实现都依赖于AudioSystem&#xff0c;AudioService通过AudioSys…

Attention Is All You Need若如爱因斯坦的相对论,Transformer模型则堪称E=MC^2之等量公式

Transformer模型已经成为当前所有自然语言处理NLP的标配&#xff0c;如GPT&#xff0c;Bert&#xff0c;Sora&#xff0c;LLama&#xff0c;Grok等。假如《Attention Is All You Need》类比为爱因斯坦的侠义相对论&#xff0c;Transformer模型则堪称EMC^2之等量公式。 看过论文…

[BT]BUUCTF刷题第6天(3.24)

第6天 Web [极客大挑战 2019]PHP Payload&#xff1a; O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";s:3:"100";}这道题考点是网站源码备份文件泄露和PHP反序列化&#xff0c;有篇介…

SpringBoot Starter解析

conditional注解解析 介绍 基于条件的注解作用: 根据是否满足某一个特定条件决定是否创建某个特定的bean意义: Springboot实现自动配置的关键基础能力 常见的conditional注解 ConditionalOnBean: 当容器中存在某个Bean才会生效ConditionalOnMissingBean: 不存在某个Bean才会…