初级代码游戏的专栏介绍与文章目录-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博客
(这里是结束)