RingQueue.hpp 15 KB

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