Sylar_网络框架学习——线程模块(三)
创始人
2024-03-26 19:26:06
0

16-25课

日志模块+配置模块整合

/*
0.创建log文件对应的配置指针	log.cpp:590
sylar::ConfigVar >::ptr g_log_define = sylar::Config::Lookup("logs", std::set(), "logs config");在构造函数中,加入该配置对应的回调函数,并将其声明为静态变量,使其在main之前初始化
struct LogIniter{LogIniter() {g_log_define->addListener(匿名函数)}
}
static LogIniter __log_init;1.加载yaml文件	sylar::Config::LoadFromYaml(root);
2.LoadFromYaml中解析yaml,将yaml格式化为string:yaml;依次取出,判断key是否存在于std::map中
3.通过ConfigVarBase基类指针调用ConfigVar::fromString()方法,执行setValue()方法
4.setValue中若旧值和新值不同,触发回调函数
*/

1) LogDefine

log的基本配置信息

struct LogDefine {std::string name;LogLevel::Level level = LogLevel::UNKNOW;std::string formatter;std::vector appenders;bool operator==(const LogDefine& oth) const {return name == oth.name&& level == oth.level&& formatter == oth.formatter&& appenders ==oth.appenders;}bool operator<(const LogDefine& oth) const {return name < oth.name;}
};

2) LogAppenderDefine

logappender的基本配置信息

struct LogAppenderDefine {int type = 0;       //1 File  2 StdoutLogLevel::Level level = LogLevel::UNKNOW;std::string formatter;std::string file;bool operator==(const LogAppenderDefine& oth) const {return type == oth.type&& level == oth.level&& formatter == oth.formatter&& file == oth.file;}
};

3) LogDefine和set之间的偏特化

template<>
class LexicalCast > {
public:std::set operator() (const std::string& v) {YAML::Node node = YAML::Load(v);std::set vec;for(size_t i = 0; i < node.size(); ++i){auto n=node[i];if(!n["name"].IsDefined()) {std::cout<<"log config error: name is null, " << n<< std::endl;continue;}LogDefine ld;ld.name = n["name"].as();ld.level = LogLevel::FromString(n["level"].IsDefined() ? n["level"].as() : "");if(n["formatter"].IsDefined()) {ld.formatter = n["formatter"].as();}if(n["appenders"].IsDefined()){for(size_t x = 0; x < n["appenders"].size(); ++x) {auto a = n["appenders"][x];if(!a["type"].IsDefined()) {std::cout << "log config error: appenders.type is null, " << a << std::endl;continue;}std::string type = a["type"].as();LogAppenderDefine lad;if(type == "FileLogAppender"){lad.type = 1;if(!a["file"].IsDefined()){std::cout << "log config error: fileappenders.file is null, " << a << std::endl;continue;}lad.file = a["file"].as();}else if(type == "StdoutLogAppender"){lad.type = 2;}else{std::cout << "log config error: appenders.type is invaild, " << a << std::endl;continue;}ld.appenders.push_back(lad);}}vec.insert(ld);}return vec;}
};template<>
class LexicalCast, std::string> {
public:std::string operator() (const std::set& v) {YAML::Node node;for(auto& i : v){YAML::Node n;n["name"] = i.name;if(i.level != LogLevel::UNKNOW) {n["level"] = LogLevel::ToString(i.level);}if(i.formatter.empty()){n["formatter"] = i.formatter;}for(auto& a:i.appenders){YAML::Node na;if(a.type==1){na["type"] = "FileLogAppender";na["file"] = a.file;}else if(a.type==2){na["type"] = "StdoutLogAppender";}if(a.level != LogLevel::UNKNOW) {na["level"] = LogLevel::ToString(a.level);}if(!a.formatter.empty()){na["formatter"] = a.formatter;}n["appenders"].push_back(na);}node.push_back(n);}std::stringstream ss;ss << node;return ss.str();}
};

4) toYamlString()

实现将当前配置输出为string

LoggerManger::toYamlString() —》 Logger::toYamlString() —》 LogAppender::toYamlString() 虚函数 --> StdoutLogAppender::toYamlString()

​ |

​ V

​ FileLogAppender::toYamlString()

线程模块

通过封装semaphore和pthread实现

1) 信号量

class Semaphore {
public:Semaphore(uint32_t count = 0);~Semaphore();void wait();void notify();
private:Semaphore(const Semaphore&) = delete;Semaphore(const Semaphore&&) = delete;Semaphore& operator=(const Semaphore&) = delete;private:sem_t m_semaphore;
}; 

2) 锁模板

通过模板的方式实现,离开锁的作用域后先解锁,再析构锁

template
struct ScopedLockImpl {
public:ScopedLockImpl(T& mutex):m_mutex(mutex) {m_mutex.lock();m_locked = true;}~ScopedLockImpl() {unlock();}void lock() {if(!m_locked) {m_mutex.lock();m_locked = true;}}void unlock() {if(m_locked) {m_mutex.unlock();m_locked = false;}}
private:T& m_mutex;bool m_locked;
};

3) 互斥锁

同一时间只允许一个线程使用共享资源,读写速度较慢

包含ScopedLockImpl< Mutex >,当锁离开了作用域后先调用ScopedLockImpl::~ScopedLockImpl()析构函数实现解锁操作,再进行析构锁

class Mutex{
public:typedef ScopedLockImpl Lock;Mutex() {pthread_mutex_init(&m_mutex, nullptr);}~Mutex() {pthread_mutex_destroy(&m_mutex);}void lock() {pthread_mutex_lock(&m_mutex);}void unlock() {pthread_mutex_unlock(&m_mutex);}
private:pthread_mutex_t m_mutex;
};

4) 读写锁

读写分离,封装pthread_rwlock

速度较快,适合读多写少的情况

//读写模板
template
struct ReadScopedLockImpl {
public:ReadScopedLockImpl(T& mutex):m_mutex(mutex) {m_mutex.rdlock();m_locked = true;}~ReadScopedLockImpl() {unlock();}void lock() {if(!m_locked) {m_mutex.rdlock();m_locked = true;}}void unlock() {if(m_locked) {m_mutex.unlock();m_locked = false;}}
private:T& m_mutex;bool m_locked;
};
template
struct WriteScopedLockImpl {
public:WriteScopedLockImpl(T& mutex):m_mutex(mutex) {m_mutex.wrlock();m_locked = true;}~WriteScopedLockImpl() {unlock();}void lock() {if(!m_locked) {m_mutex.wrlock();m_locked = true;}}void unlock() {if(m_locked) {m_mutex.unlock();m_locked = false;}}
private:T& m_mutex;bool m_locked;
};
//读写锁
class RWMutex {
public:typedef ReadScopedLockImpl ReadLock;typedef WriteScopedLockImpl WriteLock;RWMutex() {pthread_rwlock_init(&m_lock, nullptr);}~RWMutex() {pthread_rwlock_destroy(&m_lock);}void rdlock() {pthread_rwlock_rdlock(&m_lock);}void wrlock() {pthread_rwlock_wrlock(&m_lock);}void unlock() {pthread_rwlock_unlock(&m_lock);}
private:pthread_rwlock_t m_lock;
};

5) 自旋锁

适用于快任务,每次线程操作时间较短

若无法抢占任务时,锁进行自旋操作,不会立即挂起,而是一直等待上一个线程,可以设置等待时间

会发生死锁,如果等待的线程一直阻塞,自旋锁会一直自旋等待;在自旋锁还未释放时再次申请了一个自旋锁

class Spinlock {
public:typedef ScopedLockImpl Lock;Spinlock() {pthread_spin_init(&m_mutex, 0);}~Spinlock() {pthread_spin_destroy(&m_mutex);}void lock() {pthread_spin_lock(&m_mutex);}void unlock() {pthread_spin_unlock(&m_mutex);}
private:pthread_spinlock_t m_mutex;
};

6) CAS乐观锁

无锁编程,容易引发临界资源的问题

封装原子操作,核心是比较并交换,如果想要插入的位置不是最后则一直向后移动,直至末尾开始插入

/*
A:当前值
V:内存值
B:新值
CAS(A, V, B){if(A == V){V = B;}else {重试或放弃}
}
ABA问题:A从5改为10 成功,B从5改为10 阻塞,C从10改为5 成功;这时候B采用CAS(5,5,10)判断成功,将5又改为了10,出现问题
解决:CAS判断时加入版本号,进行版本比较
*/
class CASLock {
public:typedef ScopedLockImpl Lock;CASLock() {m_mutex.clear();}~CASLock() {}void lock() {while(std::atomic_flag_test_and_set_explicit(&m_mutex, std::memory_order_acquire));}void unlock() {std::atomic_flag_clear_explicit(&m_mutex, std::memory_order_release);}
private:volatile std::atomic_flag m_mutex;
};

7) 线程

class Thread{
public:typedef std::shared_ptr ptr;Thread(std::function cb, const std::string& name);~Thread();pid_t getId() const {return m_id;}const std::string& getName() const {return m_name;}void join();static Thread* GetThis();static const std::string& GetName();static void SetName(const std::string& name);
private:Thread(const Thread&) = delete;Thread(const Thread&&) = delete;Thread& operator=(const Thread&) = delete;static void* run(void* arg);
private:pid_t m_id = -1;pthread_t m_thread = 0;std::function m_cb;std::string m_name;Semaphore m_semaphore;
};

日志+配置+线程整合

1) 日志

使用自旋锁,在LogAppender、Logger、LoggerManger类中加入

多线程情况时,访问资源的函数都需要考虑加锁

std::string Logger::toYamlString();
void Logger::clearAppenders();
void Logger::setFormatter(LogFormatter::ptr val);
LogFormatter::ptr Logger::getFormatter();
void LogAppender::setFormatter(LogFormatter::ptr val);
LogFormatter::ptr LogAppender::getFormatter();
void Logger::log(LogLevel::Level level, LogEvent::ptr event);
void Logger::addAppender(LogAppender::ptr appender);
void Logger::delAppender(LogAppender::ptr appender);
void StdoutLogAppender::log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event);
std::string StdoutLogAppender::toYamlString();
void FileLogAppender::log(Logger::ptr logger, LogLevel::Level level, LogEvent::ptr event);
std::string FileLogAppender::toYamlString();
bool FileLogAppender::reopen();
Logger::ptr LoggerManger::getLogger(const std::string& name);
std::string LoggerManger::toYamlString();

2) 配置

使用读写锁,读多写少的情况

//ConfigVar类:
std::string toString();
const T getValue();
void setValue(const T& v);
uint64_t addListener(on_change_cb cb);
void delListtener(uint64_t key);
on_change_cb getListener(uint64_t key);
void clearListener();
//Config类
void Config::Visit(std::function cb)
template
static typename ConfigVar::ptr Lookup(const std::string& name,const T& default_value, const std::string& description = "");
templatestatic typename ConfigVar::ptr Lookup(const std::string& name);
//ConfigVarBase类
ConfigVarBase::ptr Config::LookupBase(const std::string& name);

全部实现可见仓库,欢迎点赞。

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...
有效的括号 一、题目 给定一个只包括 '(',')','{','}'...
【Ctfer训练计划】——(三... 作者名:Demo不是emo  主页面链接:主页传送门 创作初心ÿ...