TWTimer.h 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. #include <chrono>
  2. #include <functional>
  3. #include <list>
  4. #include <mutex>
  5. #include <thread>
  6. #include <vector>
  7. #include <iostream>
  8. #include "ThreadPool.h"
  9. #include "spdlog/spdlog.h"
  10. #include "RingQueue.hpp"
  11. /**
  12. * @brief 这是一个时间轮定时器的实现,将任务添加到时间轮中,时间轮会在指定的时间点执行任务
  13. * 任务是一个函数,可以是lambda表达式,也可以是std::bind绑定的函数
  14. * 任务是挂在一个个时间点上的,同一个时间点可以有多个任务,时间点的最大数目就是任务容器最大容量
  15. * 间隔时间是1ms,最大定时时间是1000 * 60 * 60 ms,也就是1小时
  16. *
  17. * 缺点:
  18. * 1、时间轮的时间点是固定的,如果任务的时间点不在时间轮的时间点上,就会被延迟执行
  19. * 2、任务执行是在这个线程中执行的,如果任务执行时间过长,会影响时间轮的执行
  20. */
  21. /** ==================================================================
  22. *
  23. * ================================================================== */
  24. /* 定义函数指针 */
  25. using TaskFunc = std::function<void()>;
  26. /* 定义任务结构体 */
  27. struct Task {
  28. bool is_loop; /* 是否循环定时 */
  29. TaskFunc func; /* 任务函数 */
  30. long interval; /* 定时间隔 */
  31. };
  32. class TimerWheel {
  33. public:
  34. /* 构造函数,第一个参数是任务时间点容器最大数量,第二个参数是最低检测时间单位 */
  35. explicit TimerWheel()
  36. : m_current_index(0) {
  37. m_wheel_size = 1000 * 60 * 60; /* 1小时 */
  38. m_interval_ms = 1000; /* 1ms */
  39. m_firstLevelWheel.resize(1000);
  40. m_secondLevelWheel.resize(60);
  41. m_thirdLevelWheel.resize(60);
  42. }
  43. ~TimerWheel() {
  44. Stop();
  45. }
  46. /* 开启时间轮 */
  47. void Start() {
  48. if (m_running) {
  49. return;
  50. }
  51. m_running = true;
  52. /* 开启新线程,定时检测队列 */
  53. m_thread = std::thread([this]() {
  54. while (m_running) {
  55. std::this_thread::sleep_for(std::chrono::milliseconds(m_interval_ms));
  56. Tick();
  57. }
  58. std::cout << "timer oooops!" << std::endl;
  59. });
  60. m_thread.detach();
  61. }
  62. void Stop() {
  63. if (!m_running) {
  64. return;
  65. }
  66. m_running = false;
  67. if (m_thread.joinable()) {
  68. m_thread.join();
  69. }
  70. }
  71. /* 添加任务函数 */
  72. void AddTask(int timeout_ms, Task task, bool is_loop = false) {
  73. std::lock_guard<std::mutex> lock(mutex_);
  74. /* 计算出需要轮训的次数 */
  75. size_t ticks = timeout_ms / m_interval_ms;
  76. /* 在当前时间点上加上ticks */
  77. size_t index = (m_current_index + ticks) % m_wheel_size;
  78. /* 这里是设置在整个时间轮中,该任务在这个时间轮中的分布,以实现循环定时 */
  79. size_t allindex = index;
  80. for (size_t i = 1 ; allindex < m_wheel_size; i++)
  81. {
  82. allindex = index * i;
  83. if (allindex >= m_wheel_size)
  84. break;
  85. wheel_[allindex].push_back(task);
  86. }
  87. }
  88. private:
  89. /* 时间片 */
  90. void Tick() {
  91. std::lock_guard<std::mutex> lock(mutex_);
  92. /* 取出这个时间点的函数,循环执行 */
  93. auto& tasks = wheel_[m_current_index];
  94. for (const auto& task : tasks) {
  95. task();
  96. }
  97. //tasks.clear();
  98. /* 可以循环定时的关键,超过了设置的最大时间点,就会重置 */
  99. m_current_index = (m_current_index + 1) % m_wheel_size;
  100. }
  101. private:
  102. long m_max_interval = 0; /* 最大的定时时间长度,等于下面两个之和,超过就报错 */
  103. size_t m_wheel_size; /* 时间轮最大的轮训次数 */
  104. int m_interval_ms; /* 每个时间点的间隔秒数,这里是1ms */
  105. long m_firstLevelCount; /* 第一层级的时间轮的个数 */
  106. long m_secondLevelCount; /* 第二层级的时间轮的个数 */
  107. long m_thirdLevelCount; /* 第三层级的时间轮的个数 */
  108. std::vector<std::list<Task>> m_firstLevelWheel; /* 第一层级的时间轮,这里是ms等级,总共1000个 */
  109. std::vector<std::list<Task>> m_secondLevelWheel; /* 第二层级的时间轮,这里是秒的等级,总共60个 */
  110. std::vector<std::list<Task>> m_thirdLevelWheel; /* 第三层级的时间轮,这里是分钟的等级,总共60个 */
  111. size_t m_current_index; /* 现在的时间点 */
  112. bool m_running = false; /* 运行标识位 */
  113. std::thread m_thread;
  114. std::mutex mutex_;
  115. };