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

初级代码游戏的专栏介绍与文章目录-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.mzph.cn/news/834153.shtml

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

相关文章

25852-47-5,MAc-PEG-MAc通常作为高分子材料的交联剂,以提高材料的力学性能和稳定性

【试剂详情】 英文名称 Methacrylate-PEG-Methacrylate&#xff0c;MAc-PEG-MAc 中文名称 甲基丙烯酸酯-聚乙二醇-甲基丙烯酸酯 CAS号 25852-47-5 外观性状 由分子量决定&#xff0c;固体或者液体。 分子量 0.4k&#xff0c;0.6k&#xff0c;1k&#xff0c;2k&#xf…

基于51单片机的温度检测自动调节设计—温度上下限报警自动控制

基于51单片机的温度自动调节 &#xff08;仿真&#xff0b;程序原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 1.DS18B20检测温度给单片机处理&#xff1b; 2.LCD1602显示实时温度、温度上下限&#xff1b; 3.三个按键可设置温度上下限&#xff1…

【项目】使用Yolov8 + tesseract 实现“营业执照”信息解析(OCR) + 输入可为图片或者pdf + 完整代码 + 整体方案 + 全网首发

本项目可用于毕业设计参考、实验等,营业执照分为横版和竖版,整体检测+识别效果如下所示: 说明:图片来源于网络,如有侵权,请联系作者删除。 目录

【JUC】并发编程 Synchronized 锁升级原理

Synchronized如何实现同步/互斥的效果&#xff1f; monitorenter&#xff1a; 将锁对象对象头中Mark Word的前30bit替换成指向操作系统中与其关联的monitor对象&#xff0c;将锁记录位状态改为10 monitorexit&#xff1a; 将锁对象对象头中Mark Word进行重置&#xff0c;重新恢…

蓝桥杯练习系统(算法训练)ALGO-946 Q神的足球赛

资源限制 内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述 足球赛上&#xff0c;只见Q神如闪电般的速度带球时而左&#xff0c;时而右&#xff0c;时而前&#xff0c;时而后&#xff…

第二证券|摘星脱帽行情火爆 超40只ST股候场

5月8日&#xff0c;ST中嘉、*ST明诚强势涨停&#xff0c;2家公司年内均请求吊销危险警示。其间ST中嘉自4月29日以来&#xff0c;已接连录得5个涨停板&#xff1b;*ST明诚自4月23日以来9个交易日录得8个涨停板。 年报季向来是几家欢喜几家愁的时间&#xff0c;有公司披星戴帽&a…

【优选算法】——Leetcode——LCR 179. 查找总价格为目标值的两个商品

1.题目 2. 解法⼀&#xff08;暴⼒解法&#xff0c;会超时&#xff09;&#xff1a; 1.算法思路&#xff1a; 2.图解 3. 代码实现 3. 解法⼆&#xff08;双指针-对撞指针&#xff09;&#xff1a; 1.算法思路&#xff1a; 2.图解 3.代码实现 1.C语言 2…

如何快速学习VCU电控开发

本课程基于实际项目案例和岗位需求技能制定教学大纲&#xff0c;以任务驱动方式引导学员&#xff0c;让学员快速掌握VCU开发知识。首先从VCU开发必备知识点和MATLAB/Simulink软件建模工具的使用入手&#xff0c;夯实学员基础。再通过策略设计、模型搭建和测试标定来指导学员完成…

AI图书推荐:使用FastAPI框架构建AI服务

《使用FastAPI构建生成式AI服务》&#xff08;Building Generative AI Services with FastAPI (Early Release) &#xff09;是一本由Ali Parandeh编写的书籍&#xff0c;计划于2025年3月首次出版&#xff0c;该书以实践为导向&#xff0c;指导读者如何开发具备丰富上下文信息的…

【k8s多集群管理平台开发实践】十二、开发总结及注意事项【完结】

文章目录 简介总结前面11章节所实现的功能&#xff1a; 一.完善集群更多功能1.1.可以扩展更多的功能 二.该系列课程代码地址三.技术栈及开发经验3.1.开发过程中所使用到的一些技术栈 四.开发过程中需要注意的事项五.反馈与交流 简介 该系列文章主要是介绍了多k8s集群平台开发的…

【前端】CSS基础(1)

文章目录 前言一、CSS基础1、 CSS是什么2、 CSS基本语法规范3、 代码风格3.1 样式格式3.2 样式大小写3.3 空格规范 4、 CSS引入方式4.1 内部样式表4.2 行内样式表4.3 外部样式 前言 这篇博客仅仅是对CSS的基本结构进行了一些说明&#xff0c;关于CSS的更多讲解以及HTML、Javasc…

iOS MRC那句话

混编时使用MRC文件需要使用这句话 -fno-objc-arc在下图中显示的位置添加

Pytorch常用的函数(九)torch.gather()用法

Pytorch常用的函数(九)torch.gather()用法 torch.gather() 就是在指定维度上收集value。 torch.gather() 的必填也是最常用的参数有三个&#xff0c;下面引用官方解释&#xff1a; input (Tensor) – the source tensordim (int) – the axis along which to indexindex (Lo…

JumpServer发布web应用

项目背景&#xff1a; 由于防火墙密码安全没有达到审计要求&#xff0c;需要加固防火墙用户安全&#xff0c;通过JumpServer发布防火墙登录页面&#xff0c;提供远程访问 认证要求&#xff1a; 1、密码记忆多次 2、密码大小写 3、密码字符 4、密码数字 加固前密码策略&…

详解BOM编程

华子目录 BOM编程window对象常见的window对象的属性常见的window对象的方法注意 history对象history对象的属性history对象的方法 screen 对象navigator 对象属性方法 location对象属性方法示例 BOM编程 JavaScript本质是在浏览器中运行&#xff0c;所以JavaScript提供了BOM&a…

初学java

注意点 1.使用关键字long的时候&#xff0c;在其赋值的时候要在后面加上大写或者小写的l&#xff0c;个人推荐大写&#xff0c;小写与数‘1’难区分。 2.函数的名字要与文件夹的名字相同&#xff0c;并且文件夹后面一定要有.java。例如这个的名字是Main,函数就得用这个&#x…

docker学习笔记(四)制作镜像

目录 第1步&#xff1a;编辑Dockerfile 第2步&#xff1a;编辑requirements.txt文件 第3步&#xff1a;编辑app.py文件&#xff0c;我们的程序文件 第4步&#xff1a;生成镜像文件 第5步&#xff1a;使用镜像&#xff0c;启动容器 第6步&#xff1a; 启动redis容器、将容器…

短信群发平台:全功能SDK短信接口解决方案

SDK短信接口介绍&#xff1a; 为了满足不同企业的需求&#xff0c;我们提供了一站式SDK短信接口解决方案。这些接口不仅功能强大&#xff0c;而且易于集成到现有的企业系统中&#xff0c;以提供更加安全、高效和便捷的服务。 1.短信验证码接口&#xff1a;用于用户注册、密码修…

动态代理,案例理解

动态代理&#xff1a;代理就是被代理者没有能力或者不愿意去完成某件事情&#xff0c;需要找个人代替自己去完成这件事&#xff0c;动态代理就是用来对业务功能&#xff08;方法&#xff09;进行代理的。 步骤&#xff1a; 1.必须有接口&#xff0c;实现类要实现接口&#xf…

Windows下,基于Gradle用Docker发布自己的程序

方案1&#xff1a; windows下打包程序&#xff0c;然后&#xff0c;上传到linux下&#xff0c;生成docker镜像&#xff0c;然后执行。 首先&#xff1a; 由于是采用Gradle管理的项目&#xff0c;打包的时候需要执行build任务。执行完成后&#xff0c;再build\libs目录下应该…