RingQueue.hpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649
  1. #ifndef RINGQUEUE_H
  2. #define RINGQUEUE_H
  3. #include <cstdlib>
  4. #include <utility>
  5. #include <mutex>
  6. #include <condition_variable>
  7. #include <atomic>
  8. /**
  9. @brief 这里采用零公摊的方式,设置多大的空间,就有多大的空间可以使用
  10. 1、m_rear指向最新入队的元素的下一个位置,就是下个将要入队的元素位置
  11. 2、m_front指向需要出队的第一个元素
  12. 3、环形队列自带互斥锁
  13. 注意:
  14. 使用时要注意,不带NoBlock的都是阻塞函数
  15. 判断队列满:
  16. m_rear == m_front,并且此时都不等于 -1
  17. 判断队列空:
  18. m_rear == m_front,并且都等于 -1
  19. 获取队列大小:
  20. 基本原则就是m_rear后面跟着的是有效值,m_front后面跟着的是已经出队的大小
  21. m_rear > m_front,返回 m_rear - m_front
  22. m_front > m_rear,返回 m_capacity - (m_front - m_rear)
  23. m_rear == m_front,且不等于-1,返回 m_capacity
  24. m_rear == m_front,且等于-1,返回 0
  25. @tparam T 模版类型
  26. 关于迭代器
  27. 因为这里会自动上锁,所以没有提供迭代器,这个环形队列适用于生产者消费者模型
  28. */
  29. #define _DefaultValue (m_isUseDefaultValue.load() ? m_defaultValue : T{})
  30. template<typename T>
  31. class RingQueue
  32. {
  33. RingQueue(const RingQueue<T>& queue) = delete;
  34. RingQueue<T> operator=(const RingQueue<T>& queue) = delete;
  35. public:
  36. RingQueue();
  37. RingQueue(long size);
  38. RingQueue(long size, T defaultValue);
  39. ~RingQueue();
  40. /* 入队,默认是阻塞入队,队列满就阻塞住,直到有位置 */
  41. void push(const T& value);
  42. void push(T&& value);
  43. bool push_noBlock(const T& value);
  44. bool push_noBlock(T&& value);
  45. /* 入队,队列满就出队一个,非阻塞 */
  46. T push_pop(const T& value);
  47. /* 出队,删除队列的首个元素
  48. * 注意,如果存储的是指针,需要手动释放该指针指向的内存区域,不然会造成内存泄漏 */
  49. void pop();
  50. /* 获取队列中第一个值,但是不出队
  51. * 阻塞的方式获取,如果队列为空,会一直阻塞住,直到获取到数据为止 */
  52. T front();
  53. /* 非阻塞的方式获取,队列为空返回false */
  54. bool front_noBlock(T& t);
  55. /* 非阻塞方式获取第一个值,如果对列为空,则会返回设置的默认值 */
  56. T front_noBlock();
  57. /* 获取队列有一个数据,获取完立刻出队
  58. * 如果队列为空,会阻塞住,直到有数据为止
  59. * 如果删除了拷贝构造函数,使用会报错 */
  60. T front_pop();
  61. /* 非阻塞方式获取第一个值,并出队 */
  62. bool front_pop_noBlock(T& t);
  63. /* 非阻塞方式获取第一个值,并出队,如果队列为空,会返回设置的默认值 */
  64. T front_pop_noBlock();
  65. /* 通过移动语义获取数据,获取完成后队列中的数据只是个空壳了,因此直接就出队了 */
  66. bool front_pop_move(T& t);
  67. bool front_pop_move_noBlock(T& t);
  68. /* 设置队列大小 */
  69. void setQueueCapacity(long size);
  70. /* 设置默认值,给指针类型使用,如果是非阻塞获取,空的时候可以返回为设置的默认值(如nullptr) */
  71. void setDefaultValue(T defaultValue);
  72. /* 获取队列大小,队列中有效值的大小 */
  73. long QueueSize();
  74. /* 获取队列容量 */
  75. long QueueCapacity();
  76. /* 判断队列是否为空 */
  77. bool isEmpty();
  78. /* 判断队列是否已满 */
  79. bool isFull();
  80. /* 清空队列 */
  81. void clearQueue();
  82. /* 退出所有可能的阻塞函数 */
  83. void exit();
  84. private:
  85. /* 判断是否空 */
  86. inline bool _isEmpty();
  87. /* 判断是否满 */
  88. inline bool _isFull();
  89. private:
  90. std::atomic_bool m_isExit = false; /* 是否退出,这个标识位是为了退出阻塞住的函数 */
  91. std::mutex m_mutex; /* 互斥锁 */
  92. T m_defaultValue; /* 默认值 */
  93. std::atomic_bool m_isUseDefaultValue = false; /* 是否使用默认值 */
  94. T* m_queue = nullptr; /* 队列 */
  95. long m_capacity = 0; /* 队列容量 */
  96. long m_front = -1; /* 队头 */
  97. long m_rear = -1; /* 队尾 */
  98. std::condition_variable m_cond_NoFull; /* 非满条件变量 */
  99. std::condition_variable m_cond_NoEmpty; /* 非空条件变量 */
  100. };
  101. /* =====================================================================
  102. * ***************************** 函数实现 *****************************
  103. * ===================================================================== */
  104. /* 这个构造函数需要调用 setQueueSize 设置环形队列的大小 */
  105. template<typename T>
  106. RingQueue<T>::RingQueue()
  107. {
  108. }
  109. template<typename T>
  110. RingQueue<T>::RingQueue(long capacity)
  111. {
  112. m_capacity = capacity;
  113. m_queue = new T[m_capacity] {};
  114. }
  115. /* 添加默认值 */
  116. template<typename T>
  117. RingQueue<T>::RingQueue(long capacity, T defaultValue)
  118. {
  119. m_capacity = capacity;
  120. m_queue = new T[m_capacity] {};
  121. for(long i = 0; i < m_capacity; i++)
  122. {
  123. m_queue[i] = defaultValue;
  124. }
  125. m_defaultValue = defaultValue;
  126. m_isUseDefaultValue.store(true); // 设置使用默认值
  127. }
  128. template<typename T>
  129. RingQueue<T>::~RingQueue()
  130. {
  131. if(m_queue != nullptr)
  132. {
  133. delete[] m_queue;
  134. m_queue = nullptr;
  135. }
  136. }
  137. /* 清空队列 */
  138. template<typename T>
  139. void RingQueue<T>::clearQueue()
  140. {
  141. std::lock_guard<std::mutex> lock(m_mutex);
  142. if(m_queue != nullptr)
  143. {
  144. delete[] m_queue;
  145. m_queue = nullptr;
  146. }
  147. m_front = -1;
  148. m_rear = -1;
  149. }
  150. /*************** 入队 *******************/
  151. template<typename T>
  152. void RingQueue<T>::push(const T& value)
  153. {
  154. {
  155. std::unique_lock<std::mutex> _lock(m_mutex);
  156. m_cond_NoFull.wait(_lock, [this]() {
  157. return (!_isFull() || m_isExit);
  158. });
  159. if(m_isExit)
  160. {
  161. return;
  162. }
  163. if(m_rear == -1)
  164. {
  165. m_front = 0;
  166. m_rear = 0;
  167. }
  168. m_queue[m_rear] = value;
  169. m_rear = (m_rear + 1) % m_capacity;
  170. }
  171. m_cond_NoEmpty.notify_all();
  172. }
  173. template<typename T>
  174. void RingQueue<T>::push(T&& value)
  175. {
  176. {
  177. std::unique_lock<std::mutex> lock(m_mutex);
  178. m_cond_NoFull.wait(lock, [this](){
  179. return (!_isFull() || m_isExit);
  180. });
  181. if(m_isExit)
  182. {
  183. return;
  184. }
  185. if(m_rear == -1)
  186. {
  187. m_front = 0;
  188. m_rear = 0;
  189. }
  190. m_queue[m_rear] = std::move(value);
  191. m_rear = (m_rear + 1) % m_capacity;
  192. }
  193. m_cond_NoEmpty.notify_all();
  194. }
  195. /**
  196. * @brief 非阻塞的方式入队,如果队列已满,直接返回
  197. *
  198. */
  199. template<typename T>
  200. bool RingQueue<T>::push_noBlock(const T& value)
  201. {
  202. {
  203. // std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock);
  204. // if(!lock.try_lock())
  205. // {
  206. // return false;
  207. // }
  208. std::lock_guard<std::mutex> lock(m_mutex);
  209. /* 先检查队列是否还有剩余空间 */
  210. if(_isFull())
  211. {
  212. return false;
  213. }
  214. else if(m_rear == -1)
  215. {
  216. m_front = 0;
  217. m_rear = 0;
  218. }
  219. m_queue[m_rear] = value;
  220. m_rear = (m_rear + 1) % m_capacity;
  221. }
  222. m_cond_NoEmpty.notify_all();
  223. return true;
  224. }
  225. template<typename T>
  226. bool RingQueue<T>::push_noBlock(T&& value)
  227. {
  228. {
  229. // std::unique_lock<std::mutex> lock(m_mutex, std::defer_lock);
  230. // if(!lock.try_lock())
  231. // {
  232. // return false;
  233. // }
  234. std::lock_guard<std::mutex> lock(m_mutex);
  235. /* 先检查队列是否还有剩余空间 */
  236. if(_isFull())
  237. {
  238. return false;
  239. }
  240. else if(m_rear == -1)
  241. {
  242. m_front = 0;
  243. m_rear = 0;
  244. }
  245. m_queue[m_rear] = std::move(value);
  246. m_rear = (m_rear + 1) % m_capacity;
  247. }
  248. m_cond_NoEmpty.notify_all();
  249. return true;
  250. }
  251. /* 入队,队列满就出队一个,非阻塞 */
  252. template<typename T>
  253. T RingQueue<T>::push_pop(const T& value)
  254. {
  255. T ret = _DefaultValue;
  256. {
  257. std::unique_lock<std::mutex> lock(m_mutex);
  258. /* 如果队列已满,先出队一个元素 */
  259. if(_isFull())
  260. {
  261. ret = std::move(m_queue[m_front]);
  262. m_front = (m_front + 1) % m_capacity;
  263. if(m_front == m_rear)
  264. {
  265. m_front = -1;
  266. m_rear = -1;
  267. }
  268. }
  269. /* 入队新元素 */
  270. if(m_rear == -1)
  271. {
  272. m_front = 0;
  273. m_rear = 0;
  274. }
  275. m_queue[m_rear] = value;
  276. m_rear = (m_rear + 1) % m_capacity;
  277. }
  278. m_cond_NoEmpty.notify_all();
  279. return ret;
  280. }
  281. /**
  282. * @brief 出队,删除队列的首个元素
  283. * 注意,如果存储的是指针,需要手动释放该指针指向的内存区域,不然会造成内存泄漏
  284. *
  285. * @tparam T
  286. */
  287. template<typename T>
  288. void RingQueue<T>::pop()
  289. {
  290. {
  291. std::unique_lock<std::mutex> lock(m_mutex);
  292. if(_isEmpty())
  293. {
  294. return;
  295. }
  296. m_front = (m_front + 1) % m_capacity;
  297. if(m_front == m_rear)
  298. {
  299. m_front = -1;
  300. m_rear = -1;
  301. }
  302. }
  303. m_cond_NoFull.notify_all();
  304. }
  305. /* 获取队列中第一个值,但是不出队
  306. * 阻塞的方式获取,如果队列为空,会一直阻塞住,直到获取到数据为止 */
  307. template<typename T>
  308. T RingQueue<T>::front()
  309. {
  310. std::unique_lock<std::mutex> lock(m_mutex);
  311. m_cond_NoEmpty.wait(lock, [this](){
  312. return (!_isEmpty() || m_isExit);
  313. });
  314. if(m_isExit)
  315. {
  316. return _DefaultValue;
  317. }
  318. return m_queue[m_front];
  319. }
  320. /* 获取队列中第一个值,但是不出队,非阻塞的方式获取 */
  321. template<typename T>
  322. bool RingQueue<T>::front_noBlock(T& t)
  323. {
  324. {
  325. std::unique_lock<std::mutex> lock(m_mutex);
  326. if(_isEmpty())
  327. {
  328. return false;
  329. }
  330. t = m_queue[m_front];
  331. }
  332. return true;
  333. }
  334. /* 非阻塞方式获取第一个值,如果对列为空,则会返回设置的默认值 */
  335. template<typename T>
  336. T RingQueue<T>::front_noBlock()
  337. {
  338. std::unique_lock<std::mutex> lock(m_mutex);
  339. if(_isEmpty())
  340. {
  341. return _DefaultValue;
  342. }
  343. return m_queue[m_front];
  344. }
  345. /* 获取对立第一个数据,获取完立刻出队
  346. * 如果队列为空,会阻塞住,直到有数据为止 */
  347. template<typename T>
  348. T RingQueue<T>::front_pop()
  349. {
  350. // T ret = _DefaultValue;
  351. {
  352. std::unique_lock<std::mutex> lock(m_mutex);
  353. m_cond_NoEmpty.wait(lock, [this](){
  354. return (!_isEmpty() || m_isExit);
  355. });
  356. /* 是否退出 */
  357. if(m_isExit)
  358. {
  359. return _DefaultValue;
  360. }
  361. /* 临时记录索引 */
  362. long front = m_front;
  363. m_front = (m_front + 1) % m_capacity;
  364. if(m_front == m_rear)
  365. {
  366. m_front = -1;
  367. m_rear = -1;
  368. }
  369. m_cond_NoFull.notify_all();
  370. return std::move(m_queue[front]);
  371. }
  372. }
  373. /* 非阻塞方式获取第一个值,并出队 */
  374. template<typename T>
  375. bool RingQueue<T>::front_pop_noBlock(T& t)
  376. {
  377. {
  378. std::unique_lock<std::mutex> lock(m_mutex);
  379. if(_isEmpty())
  380. {
  381. return false;
  382. }
  383. t = std::move(m_queue[m_front]);
  384. m_front = (m_front + 1) % m_capacity;
  385. if(m_front == m_rear)
  386. {
  387. m_front = -1;
  388. m_rear = -1;
  389. }
  390. }
  391. m_cond_NoFull.notify_all();
  392. return true;
  393. }
  394. /* 非阻塞方式获取第一个值,并出队 */
  395. template<typename T>
  396. T RingQueue<T>::front_pop_noBlock()
  397. {
  398. // T ret = _DefaultValue;
  399. {
  400. std::unique_lock<std::mutex> lock(m_mutex);
  401. // m_cond_NoEmpty.wait(lock, [this](){
  402. // return (!_isEmpty() || m_isExit);
  403. // });
  404. if(_isEmpty())
  405. {
  406. return _DefaultValue;
  407. }
  408. /* 临时记录索引 */
  409. long front = m_front;
  410. m_front = (m_front + 1) % m_capacity;
  411. if(m_front == m_rear)
  412. {
  413. m_front = -1;
  414. m_rear = -1;
  415. }
  416. m_cond_NoFull.notify_all();
  417. return std::move(m_queue[front]);
  418. }
  419. }
  420. /* 通过移动语义获取数据,获取完成后队列中的数据只是个空壳了,因此直接就出队了 */
  421. template<typename T>
  422. bool RingQueue<T>::front_pop_move(T& t)
  423. {
  424. std::unique_lock<std::mutex> lock(m_mutex);
  425. m_cond_NoEmpty.wait(lock, [this]() {
  426. return (!_isEmpty() || m_isExit);
  427. });
  428. /* 是否退出 */
  429. if(m_isExit)
  430. {
  431. return false;
  432. }
  433. t = std::move(m_queue[m_front]); // 移动语义获取数据
  434. m_front = (m_front + 1) % m_capacity;
  435. if(m_front == m_rear)
  436. {
  437. m_front = -1;
  438. m_rear = -1;
  439. }
  440. m_cond_NoFull.notify_all();
  441. return true;
  442. }
  443. template<typename T>
  444. bool RingQueue<T>::front_pop_move_noBlock(T& t)
  445. {
  446. std::unique_lock<std::mutex> lock(m_mutex);
  447. // m_cond_NoEmpty.wait(lock, [this](){
  448. // return (!_isEmpty() || m_isExit);
  449. // });
  450. if(_isEmpty())
  451. {
  452. return false;
  453. }
  454. t = std::move(m_queue[m_front]); // 移动语义获取数据
  455. m_front = (m_front + 1) % m_capacity;
  456. if(m_front == m_rear)
  457. {
  458. m_front = -1;
  459. m_rear = -1;
  460. }
  461. m_cond_NoFull.notify_all();
  462. return true;
  463. }
  464. /**
  465. * @brief 设置队列大小
  466. * 注意:使用这个设置,如果队列中存储的是指针,指针的内存区域需要在调用这个函数之前释放,不然可能会造成
  467. * 内存泄漏
  468. *
  469. * @tparam T
  470. * @param size
  471. */
  472. template<typename T>
  473. void RingQueue<T>::setQueueCapacity(long size)
  474. {
  475. if(m_queue != nullptr)
  476. {
  477. delete[] m_queue;
  478. m_queue = nullptr;
  479. }
  480. m_capacity = size;
  481. m_front = -1;
  482. m_rear = -1;
  483. m_queue = new T[m_capacity];
  484. }
  485. /* 设置默认值,给指针类型使用,如果是非阻塞获取,空的时候可以返回为设置的默认值(如nullptr) */
  486. template<typename T>
  487. void RingQueue<T>::setDefaultValue(T defaultValue)
  488. {
  489. m_defaultValue = defaultValue;
  490. m_isUseDefaultValue.store(true); // 设置使用默认值
  491. }
  492. /* 获取队列中有效值的大小 */
  493. template<typename T>
  494. long RingQueue<T>::QueueSize()
  495. {
  496. std::lock_guard<std::mutex> lock(m_mutex);
  497. if(m_rear == -1)
  498. {
  499. return 0;
  500. }
  501. else if(m_rear > m_front)
  502. {
  503. return m_rear - m_front;
  504. }
  505. else if(m_rear < m_front)
  506. {
  507. return m_capacity - ( m_front - m_rear );
  508. }
  509. /* 这时候是队列满 */
  510. return m_capacity;
  511. }
  512. /* 获取队列容量 */
  513. template<typename T>
  514. long RingQueue<T>::QueueCapacity()
  515. {
  516. std::lock_guard<std::mutex> lock(m_mutex);
  517. return m_capacity;
  518. }
  519. /**
  520. * @brief 判断队列是否为空
  521. *
  522. * @tparam T
  523. * @return true
  524. * @return false
  525. */
  526. template<typename T>
  527. bool RingQueue<T>::isEmpty()
  528. {
  529. std::lock_guard<std::mutex> lock(m_mutex);
  530. return _isEmpty();
  531. }
  532. /**
  533. * @brief 判断队列是否已满,这里判断依赖入队和出队后的队头和队尾指针的位置
  534. * 1、队头和队尾指针相等,但是队尾指针不等于-1,表示队列已满
  535. *
  536. * @tparam T
  537. * @return true
  538. * @return false
  539. */
  540. template<typename T>
  541. bool RingQueue<T>::isFull()
  542. {
  543. std::lock_guard<std::mutex> lock(m_mutex);
  544. return _isFull();
  545. }
  546. /* 退出所有可能的阻塞函数 */
  547. template<typename T>
  548. void RingQueue<T>::exit()
  549. {
  550. m_isExit = true;
  551. m_cond_NoFull.notify_all();
  552. m_cond_NoEmpty.notify_all();
  553. }
  554. /* 判断是否空 */
  555. template<typename T>
  556. bool RingQueue<T>::_isEmpty()
  557. {
  558. if((m_front == m_rear) && (m_front == -1))
  559. {
  560. return true;
  561. }
  562. return false;
  563. }
  564. /* 判断是否满 */
  565. template<typename T>
  566. inline bool RingQueue<T>::_isFull()
  567. {
  568. /* 如果m_rear或者m_front不等于-1,说明此时里面有内容
  569. * 同时m_front == m_rear,队列就满了 */
  570. if(m_front == m_rear && m_rear != -1)
  571. {
  572. return true;
  573. }
  574. return false;
  575. }
  576. #endif /* RINGQUEUE_H */