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


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

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

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

    • 原子对象是面向CPU的,非常晦涩,实际上,我认为这部分的设计是失败的
    • 原子对象的全部功能没有一种CPU是完全实现的,所以,搞清楚也没什么用
    • 就用最严格的简单互斥就行了,别给自己找麻烦

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

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

            读写锁代码:

    1. #include
    2. #include
    3. struct mySEM
    4. {
    5. public:
    6. atomic_flag flag{false};
    7. time_t ctime{ 0 };
    8. bool OnW{false};
    9. long R_count{ 0 };
    10. long W_wait{ 0 };
    11. private:
    12. bool _Lock()
    13. {
    14. //cout << (long)this << " _Lock ..." << endl;
    15. while (flag.test_and_set())
    16. {
    17. this_thread::yield();
    18. }
    19. //cout << (long)this << "_Lock down" << endl;
    20. return true;
    21. }
    22. bool _UnLock()
    23. {
    24. //cout << (long)this << "_Lock release" << endl;
    25. flag.clear();
    26. return true;
    27. }
    28. public:
    29. void init()
    30. {
    31. flag.clear();
    32. ctime = time(nullptr);
    33. OnW = false;
    34. R_count = 0;
    35. W_wait = 0;
    36. }
    37. bool RLock(bool no_wait)
    38. {
    39. _Lock();
    40. while (!(!OnW && 0 == W_wait))
    41. {
    42. _UnLock();
    43. if (no_wait)return false;
    44. this_thread::yield();
    45. _Lock();
    46. }
    47. ++R_count;
    48. _UnLock();
    49. return true;
    50. }
    51. bool RUnLock()
    52. {
    53. _Lock();
    54. --R_count;
    55. _UnLock();
    56. return true;
    57. }
    58. bool WLock(bool no_wait)
    59. {
    60. _Lock();
    61. ++W_wait;
    62. _UnLock();
    63. _Lock();
    64. while (!(!OnW && 0 == R_count))
    65. {
    66. if (no_wait)
    67. {
    68. --W_wait;
    69. _UnLock();
    70. return false;
    71. }
    72. _UnLock();
    73. this_thread::yield();
    74. _Lock();
    75. }
    76. OnW = true;
    77. --W_wait;
    78. _UnLock();
    79. return true;
    80. }
    81. bool WUnLock()
    82. {
    83. _Lock();
    84. OnW = false;
    85. _UnLock();
    86. return true;
    87. }
    88. };

            解释一下:

    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() 写锁定/解锁

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

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

    1. #pragma once
    2. #include
    3. #include
    4. //对象实例不可复制不可移动,内部记录进程操作状态和线程操作状态
    5. class CZS_RWMutex2
    6. {
    7. public:
    8. struct mySEM
    9. {
    10. public:
    11. atomic_flag flag{false};
    12. time_t ctime{ 0 };
    13. bool OnW{false};
    14. long R_count{ 0 };
    15. long W_wait{ 0 };
    16. private:
    17. bool _Lock()
    18. {
    19. //cout << (long)this << " _Lock ..." << endl;
    20. while (flag.test_and_set())
    21. {
    22. this_thread::yield();
    23. }
    24. //cout << (long)this << "_Lock down" << endl;
    25. return true;
    26. }
    27. bool _UnLock()
    28. {
    29. //cout << (long)this << "_Lock release" << endl;
    30. flag.clear();
    31. return true;
    32. }
    33. public:
    34. void init()
    35. {
    36. flag.clear();
    37. ctime = time(nullptr);
    38. OnW = false;
    39. R_count = 0;
    40. W_wait = 0;
    41. }
    42. bool RLock(bool no_wait)
    43. {
    44. _Lock();
    45. while (!(!OnW && 0 == W_wait))
    46. {
    47. _UnLock();
    48. if (no_wait)return false;
    49. this_thread::yield();
    50. _Lock();
    51. }
    52. ++R_count;
    53. _UnLock();
    54. return true;
    55. }
    56. bool RUnLock()
    57. {
    58. _Lock();
    59. --R_count;
    60. _UnLock();
    61. return true;
    62. }
    63. bool WLock(bool no_wait)
    64. {
    65. _Lock();
    66. ++W_wait;
    67. _UnLock();
    68. _Lock();
    69. while (!(!OnW && 0 == R_count))
    70. {
    71. if (no_wait)
    72. {
    73. --W_wait;
    74. _UnLock();
    75. return false;
    76. }
    77. _UnLock();
    78. this_thread::yield();
    79. _Lock();
    80. }
    81. OnW = true;
    82. --W_wait;
    83. _UnLock();
    84. return true;
    85. }
    86. bool WUnLock()
    87. {
    88. _Lock();
    89. OnW = false;
    90. _UnLock();
    91. return true;
    92. }
    93. };
    94. private:
    95. mutable mySEM* sem_id{ nullptr };//信号量ID
    96. mutable bool isIngore{ false };//是否忽略,不锁定
    97. mutable bool isSafe{ false };//是否带有安全检查,确保操作序列正确
    98. //进程操作计数,防止操作顺序错误
    99. mutable atomic<int> count_WLock{ 0 };
    100. mutable atomic<int> count_RLock{ 0 };
    101. //线程操作记录,防止线程操作错误并可用于中途让出再重新锁定
    102. struct thread_data
    103. {
    104. bool _isLocked{ false };//是否已经锁定,若已经锁定则不重复锁定
    105. bool _isWLock{ false };//是否是写锁定,当isLocked时有效
    106. bool isLocked()const
    107. {
    108. return _isLocked;
    109. }
    110. bool isWLocked()const
    111. {
    112. return _isLocked && _isWLock;
    113. }
    114. bool isRLocked()const
    115. {
    116. return _isLocked && !_isWLock;
    117. }
    118. void thread_data_WLock()
    119. {
    120. _isLocked = true;
    121. _isWLock = true;
    122. }
    123. void thread_data_RLock()
    124. {
    125. _isLocked = true;
    126. _isWLock = false;
    127. }
    128. void thread_data_UnLock()
    129. {
    130. _isLocked = false;
    131. _isWLock = false;
    132. }
    133. };
    134. public:
    135. thread_data* getThreadData()const
    136. {
    137. thread_local mapconst*, thread_data > d;//通过对象地址区分不同的对象
    138. return &d[this];
    139. }
    140. //禁止移动和复制(不能用于vector,因为vector会移动对象)
    141. CZS_RWMutex2() = default;
    142. CZS_RWMutex2(CZS_RWMutex2 const&) = delete;
    143. CZS_RWMutex2& operator =(CZS_RWMutex2 const&) = delete;
    144. CZS_RWMutex2(CZS_RWMutex2 const&&) = delete;
    145. CZS_RWMutex2& operator =(CZS_RWMutex2 const&&) = delete;
    146. ~CZS_RWMutex2()
    147. {
    148. if (0 != count_WLock || 0 != count_RLock)
    149. {
    150. if (0 != count_WLock) cout << "警告:析构而未解锁:" << sem_id << " is w locked " << count_WLock << endl;
    151. if (0 != count_RLock) cout << "警告:析构而未解锁:" << sem_id << " is r locked " << count_RLock << endl;
    152. }
    153. sem_id = nullptr;
    154. }
    155. private:
    156. mutable int m_errid{ 0 };//最近的错误号
    157. mutable CZS_StringStream m_errmsg;//最近的错误信息
    158. string errno2str()const
    159. {
    160. string s;
    161. switch (errno)
    162. {
    163. case EACCES: s = "EACCES"; break;
    164. case EINVAL: s = "EINVAL"; break;
    165. case EPERM: s = "EPERM"; break;
    166. case EOVERFLOW: s = "EOVERFLOW"; break;
    167. case ERANGE: s = "ERANGE"; break;
    168. case E2BIG: s = "E2BIG"; break;
    169. case EAGAIN: s = "EAGAIN"; break;
    170. case EFAULT: s = "EFAULT"; break;
    171. case EFBIG: s = "EFBIG"; break;
    172. case EIDRM: s = "EIDRM"; break;
    173. case EINTR: s = "EINTR"; break;
    174. case ENOSPC: s = "ENOSPC"; break;
    175. default: s = "semctl error";
    176. }
    177. return s;
    178. }
    179. public:
    180. string Report()const
    181. {
    182. char buf[1024];
    183. string ret;
    184. if (nullptr != sem_id)
    185. {
    186. sprintf(buf, "sem_id = %10ld , W %d R %d (%s), %s %s", (long)sem_id, count_WLock.load(), count_RLock.load()
    187. , (getThreadData()->isLocked() ? (getThreadData()->isWLocked() ? "W" : "R") : "-")
    188. , (isSafe ? "safe" : ""), (isIngore ? " , ingored" : ""));
    189. ret += buf;
    190. long w, w_count, r_count, w_wait;
    191. if (GetCount2(w, w_count, r_count, w_wait))
    192. {
    193. sprintf(buf, " 写锁 %ld 写计数 %ld 读计数 %ld 写等待 %ld", w, w_count, r_count, w_wait);
    194. ret += buf;
    195. }
    196. if (0 != m_errid)
    197. {
    198. sprintf(buf, " 错误:%d %s", m_errid, m_errmsg.str().c_str());
    199. }
    200. else
    201. {
    202. sprintf(buf, " 无错误");
    203. }
    204. ret += buf;
    205. }
    206. else
    207. {
    208. ret += "空信号量";
    209. }
    210. return ret;
    211. }
    212. private:
    213. void after_WLock()const
    214. {
    215. ++count_WLock;
    216. getThreadData()->thread_data_WLock();
    217. }
    218. void after_RLock()const
    219. {
    220. ++count_RLock;
    221. getThreadData()->thread_data_RLock();
    222. }
    223. void after_WUnLock()const
    224. {
    225. --count_WLock;
    226. getThreadData()->thread_data_UnLock();
    227. }
    228. void after_RUnLock()const
    229. {
    230. --count_RLock;
    231. getThreadData()->thread_data_UnLock();
    232. }
    233. public:
    234. //忽略锁定调用,不执行锁定
    235. void ingore()const { isIngore = true; }
    236. //恢复功能
    237. void enable()const { isIngore = false; }
    238. //启用安全检查
    239. void safe(bool _safe)const { isSafe = _safe; }
    240. bool isConnected()const { return nullptr != sem_id; }
    241. bool Attach(mySEM* id)
    242. {
    243. if (isSafe)
    244. {
    245. if (0 != count_WLock || 0 != count_RLock)
    246. {
    247. *(int*)&m_errid = __LINE__;
    248. (*(stringstream*)&m_errmsg).str("");
    249. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";
    250. return false;
    251. }
    252. }
    253. sem_id = id;
    254. return nullptr != sem_id;
    255. }
    256. bool Detach()
    257. {
    258. if (isSafe)
    259. {
    260. if (0 != count_WLock || 0 != count_RLock)
    261. {
    262. *(int*)&m_errid = __LINE__;
    263. (*(stringstream*)&m_errmsg).str("");
    264. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";
    265. return false;
    266. }
    267. }
    268. sem_id = nullptr;
    269. return true;
    270. }
    271. //创建新信号量
    272. bool Create(mySEM * id)
    273. {
    274. if (isSafe)
    275. {
    276. if (0 != count_WLock || 0 != count_RLock)
    277. {
    278. *(int*)&m_errid = __LINE__;
    279. (*(stringstream*)&m_errmsg).str("");
    280. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";
    281. return false;
    282. }
    283. }
    284. sem_id = id;
    285. if (nullptr == sem_id)
    286. {
    287. m_errid = __LINE__;
    288. m_errmsg.str("");
    289. m_errmsg << errno2str();
    290. return false;
    291. }
    292. sem_id->init();
    293. return true;
    294. }
    295. //复位
    296. bool Reset()
    297. {
    298. return Create(sem_id);
    299. }
    300. //删除信号量
    301. bool Destory()
    302. {
    303. if (isSafe)
    304. {
    305. if (0 != count_WLock || 0 != count_RLock)
    306. {
    307. *(int*)&m_errid = __LINE__;
    308. (*(stringstream*)&m_errmsg).str("");
    309. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";
    310. return false;
    311. }
    312. }
    313. sem_id = nullptr;
    314. return true;
    315. }
    316. //锁定,等待
    317. bool RLock()const { return _RLock(false); }
    318. bool TryRLock()const { return _RLock(true); }
    319. bool _RLock(bool no_wait)const
    320. {
    321. if (isSafe)
    322. {
    323. if (getThreadData()->isLocked())
    324. {
    325. *(int*)&m_errid = __LINE__;
    326. (*(stringstream*)&m_errmsg).str("");
    327. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,不能重复锁定";
    328. return false;
    329. }
    330. }
    331. if (isIngore)
    332. {
    333. after_RLock();
    334. return true;//忽略锁定
    335. }
    336. if (sem_id->RLock(no_wait))
    337. {
    338. after_RLock();
    339. return true;
    340. }
    341. else
    342. {
    343. return false;
    344. }
    345. }
    346. bool WLock()const { return _WLock(false); }
    347. bool TryWLock()const { return _WLock(true); }
    348. bool _WLock(bool no_wait)const
    349. {
    350. if (isSafe)
    351. {
    352. if (getThreadData()->isLocked())
    353. {
    354. *(int*)&m_errid = __LINE__;
    355. (*(stringstream*)&m_errmsg).str("");
    356. *(stringstream*)&m_errmsg << "sem " << sem_id << " 已经锁定,需要先解锁";
    357. return false;
    358. }
    359. }
    360. if (isIngore)
    361. {
    362. after_WLock();
    363. return true;//忽略锁定
    364. }
    365. if (sem_id->WLock(no_wait))
    366. {
    367. after_WLock();
    368. return true;
    369. }
    370. else
    371. {
    372. return false;
    373. }
    374. }
    375. //解除锁定
    376. bool RUnLock()const
    377. {
    378. if (isSafe)
    379. {
    380. if (!getThreadData()->isRLocked())
    381. {
    382. *(int*)&m_errid = __LINE__;
    383. (*(stringstream*)&m_errmsg).str("");
    384. *(stringstream*)&m_errmsg << "sem " << sem_id << " 未锁定或不是读锁定";
    385. return false;
    386. }
    387. }
    388. if (isIngore)
    389. {
    390. after_RUnLock();
    391. return true;//忽略锁定
    392. }
    393. if (sem_id->RUnLock())
    394. {
    395. after_RUnLock();
    396. return true;
    397. }
    398. else
    399. {
    400. *(int*)&m_errid = __LINE__;
    401. (*(stringstream*)&m_errmsg).str("");
    402. *(stringstream*)&m_errmsg << errno2str();
    403. return false;
    404. }
    405. }
    406. bool WUnLock()const
    407. {
    408. if (isSafe)
    409. {
    410. if (!getThreadData()->isWLocked())
    411. {
    412. *(int*)&m_errid = __LINE__;
    413. (*(stringstream*)&m_errmsg).str("");
    414. *(stringstream*)&m_errmsg << "sem " << sem_id << " 未锁定或不是写锁定";
    415. return false;
    416. }
    417. }
    418. if (isIngore)
    419. {
    420. after_WUnLock();
    421. return true;//忽略锁定
    422. }
    423. if (sem_id->WUnLock())
    424. {
    425. after_WUnLock();
    426. return true;
    427. }
    428. else
    429. {
    430. *(int*)&m_errid = __LINE__;
    431. (*(stringstream*)&m_errmsg).str("");
    432. *(stringstream*)&m_errmsg << errno2str();
    433. return false;
    434. }
    435. }
    436. string GetErrorMessage()const { return m_errmsg.str(); }
    437. int GetErrorID()const { return m_errid; }//获得最新的错误ID
    438. bool isFree()const
    439. {
    440. bool ignored;
    441. long w_count;
    442. long r_count;
    443. long w_wait;
    444. if (GetCount(ignored, w_count, r_count, w_wait))
    445. {
    446. return 0 == w_count + r_count + w_wait;
    447. }
    448. return false;
    449. }
    450. bool GetCount(bool& ignored, long& w_count, long& r_count, long& w_wait)const
    451. {
    452. long w;
    453. ignored = isIngore;
    454. return GetCount2(w, w_count, r_count, w_wait);
    455. }
    456. bool GetCount2(long& w, long& w_count, long& r_count, long& w_wait)const
    457. {
    458. w = 0;
    459. w_count = 0;
    460. r_count = 0;
    461. w_wait = 0;
    462. if (nullptr != sem_id)
    463. {
    464. w = !sem_id->OnW;
    465. w_count = sem_id->OnW;
    466. r_count = sem_id->R_count;
    467. w_wait = sem_id->W_wait;
    468. }
    469. return true;
    470. }
    471. };

            这部分主要是加入了跟踪调试的功能,具体解释待续。

    (这里是结束)

  • 相关阅读:
    手把手教学!新一代 Kaldi: TTS Runtime ASR 实时本地语音识别 语音合成来啦
    158_模型_Power BI 使用 DAX + SVG 打通制作商业图表几乎所有可能
    ppt录屏没有声音?超实用教程来了!
    微信小程序基于Promise封装发起网络请求
    python最常用的三种输出格式
    如何用Redis实现分布式锁?
    JavaScript 基本语法
    使用Python实现强化学习算法
    大语言模型(LLM)综述(二):开发大语言模型的公开可用资源
    【Rust日报】2022-08-20 将 Rust 带入太空 - 为 VA108XX MCU 系列建立 Rust 生态系统
  • 原文地址:https://blog.csdn.net/2301_77171572/article/details/134472732