程序设计:C++11原子 写优先的读写锁(源码详解)

embedded/2024/9/23 14:36:58/

初级代码游戏的专栏介绍与文章目录-CSDN博客

我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。

这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。


github位置:codetoys/ctfc.git src/function/mymutex.h和mymutex1-3.h  

        这是对程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)-CSDN博客的原子对象版本,写原来那个版本的时候C++11尚未发布。

        关于写优先的读写互斥对象的原理可以参考上面的链接,但使用原子对象比使用信号量更简单,因为我采用的是简单锁加上自行判断的方式。

        本文不详细讲解原子对象,只强调这么几点:

  • 原子对象是面向CPU的,非常晦涩,实际上,我认为这部分的设计是失败的
  • 原子对象的全部功能没有一种CPU是完全实现的,所以,搞清楚也没什么用
  • 对结构的原子实现需要借助隐藏的变量,在Linux上(准确地说大概应该是g++的STL实现)这个隐藏的变量是放在进程的其它位置,而不是直接添加在结构里面,所以只能实现进程内互斥,无法实现跨进程互斥。而在windows上(实际是指VS的STL实现),我推测如果数据放在内存映射文件(相当于UNIX/LINUX的共享内存),是可以跨进程互斥的。
  • 就用最严格的简单互斥就行了,别给自己找麻烦

        鉴于以上几点,我用atomic_flag做互斥控制,其功能就是锁定/解锁,相当于进出一次信号量操作,而信号量里面数值处理在代码里实现。

        为什么一定要用原子来重新实现呢?因为原子的性能比信号量实在是快了太多了。

        相关技术点:

  • C++11的原子功能 头文件<atomic> 在CentOS上并非默认支持,需要安装额外的库
  • C++11的线程功能 头文件<thread> 比POSIX线程库简单太多了
  • atomic_flag 原子的bool,可以用来实现锁定和解锁
  • atomic_flag::test_and_set() 如果是false就设置为true,整个操作是原子的,前后加了锁,不可能被中断和乱序
  • atomic_flag::clear() 设置为false,整个操作也是原子的
  • this_thread::yield() 让出线程时间片。做循环判断时用这个比死循环省CPU、比sleep定时响应快(就是为了实现所谓“自旋锁”,重试等待)

        读写锁代码:

#include <atomic>
#include <thread>struct mySEM{public:atomic_flag flag{false};time_t ctime{ 0 };bool OnW{false};long R_count{ 0 };long W_wait{ 0 };private:bool _Lock(){//cout << (long)this << " _Lock ..." << endl;while (flag.test_and_set()){this_thread::yield();}//cout << (long)this << "_Lock down" << endl;return true;}bool _UnLock(){//cout << (long)this << "_Lock release" << endl;flag.clear();return true;}public:void init(){flag.clear();ctime = time(nullptr);OnW = false;R_count = 0;W_wait = 0;}bool RLock(bool no_wait){_Lock();while (!(!OnW && 0 == W_wait)){_UnLock();if (no_wait)return false;this_thread::yield();_Lock();}++R_count;_UnLock();return true;}bool RUnLock(){_Lock();--R_count;_UnLock();return true;}bool WLock(bool no_wait){_Lock();++W_wait;_UnLock();_Lock();while (!(!OnW && 0 == R_count)){if (no_wait){--W_wait;_UnLock();return false;}_UnLock();this_thread::yield();_Lock();}OnW = true;--W_wait;_UnLock();return true;}bool WUnLock(){_Lock();OnW = false;_UnLock();return true;}};

        解释一下:

atomic_flag flag{false}; 原子对象,用来保护对其它成员变量的操作。

time_t ctime{ 0 }; 创建时间,对功能而言可以无视。

bool OnW{false}; 状态:是否写锁定中。

long R_count{ 0 }; 读计数。

long W_wait{ 0 }; 写等待。

        很容易看出来这个读写锁的逻辑和程序设计:信号量 写优先的读写互斥对象(完整源码 代码详解)-CSDN博客中用信号量的逻辑是一样的。

        私有方法:

bool _Lock()/bool _UnLock() 锁定/解锁原子对象,公有方法必须先锁定才能操作内部成员。

        公有方法:

void init() 初始化,简单测试不需要使用,因为和初值是一样的。

bool RLock(bool no_wait)/bool RUnLock() 读锁定/解锁

bool WLock(bool no_wait)/bool WUnLock() 写锁定/解锁

        感觉比信号量简单多了啊。

        当然实际使用还要考虑如何跟踪调试,所以这个类其实只是实际代码一小部分而已,完整代码如下:

#pragma once#include <atomic>
#include <thread>//对象实例不可复制不可移动,内部记录进程操作状态和线程操作状态class CZS_RWMutex2{public:struct mySEM{public:atomic_flag flag{false};time_t ctime{ 0 };bool OnW{false};long R_count{ 0 };long W_wait{ 0 };private:bool _Lock(){//cout << (long)this << " _Lock ..." << endl;while (flag.test_and_set()){this_thread::yield();}//cout << (long)this << "_Lock down" << endl;return true;}bool _UnLock(){//cout << (long)this << "_Lock release" << endl;flag.clear();return true;}public:void init(){flag.clear();ctime = time(nullptr);OnW = false;R_count = 0;W_wait = 0;}bool RLock(bool no_wait){_Lock();while (!(!OnW && 0 == W_wait)){_UnLock();if (no_wait)return false;this_thread::yield();_Lock();}++R_count;_UnLock();return true;}bool RUnLock(){_Lock();--R_count;_UnLock();return true;}bool WLock(bool no_wait){_Lock();++W_wait;_UnLock();_Lock();while (!(!OnW && 0 == R_count)){if (no_wait){--W_wait;_UnLock();return false;}_UnLock();this_thread::yield();_Lock();}OnW = true;--W_wait;_UnLock();return true;}bool WUnLock(){_Lock();OnW = false;_UnLock();return true;}};private:mutable mySEM* sem_id{ nullptr };//信号量IDmutable bool isIngore{ false };//是否忽略,不锁定mutable bool isSafe{ false };//是否带有安全检查,确保操作序列正确//进程操作计数,防止操作顺序错误mutable atomic<int> count_WLock{ 0 };mutable atomic<int> count_RLock{ 0 };//线程操作记录,防止线程操作错误并可用于中途让出再重新锁定struct thread_data{bool _isLocked{ false };//是否已经锁定,若已经锁定则不重复锁定bool _isWLock{ false };//是否是写锁定,当isLocked时有效bool isLocked()const{return _isLocked;}bool isWLocked()const{return _isLocked && _isWLock;}bool isRLocked()const{return _isLocked && !_isWLock;}void thread_data_WLock(){_isLocked = true;_isWLock = true;}void thread_data_RLock(){_isLocked = true;_isWLock = false;}void thread_data_UnLock(){_isLocked = false;_isWLock = false;}};public:thread_data* getThreadData()const{thread_local map<CZS_RWMutex2 const*, thread_data > d;//通过对象地址区分不同的对象return &d[this];}//禁止移动和复制(不能用于vector,因为vector会移动对象)CZS_RWMutex2() = default;CZS_RWMutex2(CZS_RWMutex2 const&) = delete;CZS_RWMutex2& operator =(CZS_RWMutex2 const&) = delete;CZS_RWMutex2(CZS_RWMutex2 const&&) = delete;CZS_RWMutex2& operator =(CZS_RWMutex2 const&&) = delete;~CZS_RWMutex2(){if (0 != count_WLock || 0 != count_RLock){if (0 != count_WLock) cout << "警告:析构而未解锁:" << sem_id << " is w locked " << count_WLock << endl;if (0 != count_RLock) cout << "警告:析构而未解锁:" << sem_id << " is r locked " << count_RLock << endl;}sem_id = nullptr;}private:mutable int m_errid{ 0 };//最近的错误号mutable CZS_StringStream m_errmsg;//最近的错误信息string errno2str()const{string s;switch (errno){case EACCES: s = "EACCES"; break;case EINVAL: s = "EINVAL"; break;case EPERM: s = "EPERM"; break;case EOVERFLOW: s = "EOVERFLOW"; break;case ERANGE: s = "ERANGE"; break;case E2BIG: s = "E2BIG"; break;case EAGAIN: s = "EAGAIN"; break;case EFAULT: s = "EFAULT"; break;case EFBIG: s = "EFBIG"; break;case EIDRM: s = "EIDRM"; break;case EINTR: s = "EINTR"; break;case ENOSPC: s = "ENOSPC"; break;default: s = "semctl error";}return s;}public:string Report()const{char buf[1024];string ret;if (nullptr != sem_id){sprintf(buf, "sem_id = %10ld , W %d R %d (%s), %s %s", (long)sem_id, count_WLock.load(), count_RLock.load(), (getThreadData()->isLocked() ? (getThreadData()->isWLocked() ? "W" : "R") : "-"), (isSafe ? "safe" : ""), (isIngore ? " , ingored" : ""));ret += buf;long w, w_count, r_count, w_wait;if (GetCount2(w, w_count, r_count, w_wait)){sprintf(buf, " 写锁 %ld 写计数 %ld 读计数 %ld 写等待 %ld", w, w_count, r_count, w_wait);ret += buf;}if (0 != m_errid){sprintf(buf, " 错误:%d %s", m_errid, m_errmsg.str().c_str());}else{sprintf(buf, " 无错误");}ret += buf;}else{ret += "空信号量";}return ret;}private:void after_WLock()const{++count_WLock;getThreadData()->thread_data_WLock();}void after_RLock()const{++count_RLock;getThreadData()->thread_data_RLock();}void after_WUnLock()const{--count_WLock;getThreadData()->thread_data_UnLock();}void after_RUnLock()const{--count_RLock;getThreadData()->thread_data_UnLock();}public://忽略锁定调用,不执行锁定void ingore()const { isIngore = true; }//恢复功能void enable()const { isIngore = false; }//启用安全检查void safe(bool _safe)const { isSafe = _safe; }bool isConnected()const { return nullptr != sem_id; }bool Attach(mySEM* id){if (isSafe){if (0 != count_WLock || 0 != count_RLock){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";return false;}}sem_id = id;return nullptr != sem_id;}bool Detach(){if (isSafe){if (0 != count_WLock || 0 != count_RLock){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";return false;}}sem_id = nullptr;return true;}//创建新信号量bool Create(mySEM * id){if (isSafe){if (0 != count_WLock || 0 != count_RLock){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";return false;}}sem_id = id;if (nullptr == sem_id){m_errid = __LINE__;m_errmsg.str("");m_errmsg << errno2str();return false;}sem_id->init();return true;}//复位bool Reset(){return Create(sem_id);}//删除信号量bool Destory(){if (isSafe){if (0 != count_WLock || 0 != count_RLock){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";return false;}}sem_id = nullptr;return true;}//锁定,等待bool RLock()const { return _RLock(false); }bool TryRLock()const { return _RLock(true); }bool _RLock(bool no_wait)const{if (isSafe){if (getThreadData()->isLocked()){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,不能重复锁定";return false;}}if (isIngore){after_RLock();return true;//忽略锁定}if (sem_id->RLock(no_wait)){after_RLock();return true;}else{return false;}}bool WLock()const { return _WLock(false); }bool TryWLock()const { return _WLock(true); }bool _WLock(bool no_wait)const{if (isSafe){if (getThreadData()->isLocked()){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";return false;}}if (isIngore){after_WLock();return true;//忽略锁定}if (sem_id->WLock(no_wait)){after_WLock();return true;}else{return false;}}//解除锁定bool RUnLock()const{if (isSafe){if (!getThreadData()->isRLocked()){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 未锁定或不是读锁定";return false;}}if (isIngore){after_RUnLock();return true;//忽略锁定}if (sem_id->RUnLock()){after_RUnLock();return true;}else{*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << errno2str();return false;}}bool WUnLock()const{if (isSafe){if (!getThreadData()->isWLocked()){*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << "sem " << sem_id << " 未锁定或不是写锁定";return false;}}if (isIngore){after_WUnLock();return true;//忽略锁定}if (sem_id->WUnLock()){after_WUnLock();return true;}else{*(int*)&m_errid = __LINE__;(*(stringstream*)&m_errmsg).str("");*(stringstream*)&m_errmsg << errno2str();return false;}}string GetErrorMessage()const { return m_errmsg.str(); }int GetErrorID()const { return m_errid; }//获得最新的错误IDbool isFree()const{bool ignored;long w_count;long r_count;long w_wait;if (GetCount(ignored, w_count, r_count, w_wait)){return 0 == w_count + r_count + w_wait;}return false;}bool GetCount(bool& ignored, long& w_count, long& r_count, long& w_wait)const{long w;ignored = isIngore;return GetCount2(w, w_count, r_count, w_wait);}bool GetCount2(long& w, long& w_count, long& r_count, long& w_wait)const{w = 0;w_count = 0;r_count = 0;w_wait = 0;if (nullptr != sem_id){w = !sem_id->OnW;w_count = sem_id->OnW;r_count = sem_id->R_count;w_wait = sem_id->W_wait;}return true;}};

        这部分主要是加入了跟踪调试的功能,具体解释在这里:

        程序设计:C++11原子 写优先读写锁(源码详解二:操作跟踪)-CSDN博客

(这里是结束)


http://www.ppmy.cn/embedded/38400.html

相关文章

docker————docker的安装

目录 docker的安装 1、安装yum-utils工具 2、安装yum仓库 3、安装docker引擎 4、设置开机启动&#xff0c;并立即启动 5、测试 docker的安装 docker的官网Docker Docs 我才用的linux版本是rocky&#xff0c;使用的是最小安装 1、安装yum-utils工具 [rootbogon yum.rep…

【八十二】【算法分析与设计】2421. 好路径的数目,928. 尽量减少恶意软件的传播 II,并查集的应用,元素信息绑定下标一起排序,元素通过下标进行绑定

2421. 好路径的数目 给你一棵 n 个节点的树&#xff08;连通无向无环的图&#xff09;&#xff0c;节点编号从 0 到 n - 1 且恰好有 n - 1 条边。 给你一个长度为 n 下标从 0 开始的整数数组 vals &#xff0c;分别表示每个节点的值。同时给你一个二维整数数组 edges &#xff…

Django Admin后台管理:高效开发与实践

title: Django Admin后台管理&#xff1a;高效开发与实践 date: 2024/5/8 14:24:15 updated: 2024/5/8 14:24:15 categories: 后端开发 tags: DjangoAdmin模型管理用户认证数据优化自定义扩展实战案例性能安全 第1章&#xff1a;Django Admin基础 1.1 Django Admin简介 Dj…

【excel】数据非数值导致排序失效

场景 存在待排序列的数值列&#xff0c;但排序失效&#xff0c;提示类型有问题&#xff1a; 解决 选中该列&#xff0c;数据→分列 而后发现提示消失&#xff0c;识别为数字&#xff0c;可正常排序。

VBA助力Excel工作薄另存备份和自我销毁,确保工作薄名称自始至终不被修改

VBA助力Excel工作薄另存备份和自我销毁,确保工作薄名称自始至终不被修改 工作中,你是否遇到过这样的情况,工作表改来改去,工作薄名称也从“初稿”、“终稿”、“最终版”、“上交版”等逐步变化,最后也不知道哪版是最后一版,想从修改日期查看哪个是最后一版,可逐个打开关…

SoC系统中AXI4 AXI3兼容性及exclusive access

AXI4和AXI3是高级扩展接口&#xff08;Advanced eXtensible Interface&#xff09;的两个不同版本&#xff0c;它们都是用于SoC&#xff08;System on Chip&#xff09;设计中的总线协议&#xff0c;用于处理器和其它外设之间的高速数据传输。以下是它们之间的一些主要区别&…

8. 字符串转换整数 (atoi)

题目描述 把一个字符串变成整数 字符串由英文字母&#xff08;大写和小写&#xff09;、数字&#xff08;0-9&#xff09;、’ ‘、’‘、’-’ 和 ‘.’ 组成。 规则&#xff1a; 去掉前导空格判断正负&#xff1a;去掉空格之后的第一个字符。遇到结尾或者非数字符返回 解…

【Linux 性能详解】CPU性能分析工具篇

目录 uptime mpstat 实时监控 查看特定CPU核心 pidstart 监控指定进程 组合多个监控类型 监控线程资源 按用户过滤进程 vmstart 用途 基本用法 输出字段 perf execsnoop dstat 通俗解释 技术层面解释 使用示例 总结 uptime uptime 是一个在 Linux 和 Unix…