#include #include #include #include #include #include #include /** * @brief 这是一个时间轮定时器的实现,将任务添加到时间轮中,时间轮会在指定的时间点执行任务 * 任务是一个函数对象,可以是lambda表达式,也可以是std::bind绑定的函数 * 任务是挂在一个个时间点上的,同一个时间点可以有多个任务,时间点的最大数目就是任务容器最大容量 * * 缺点: * 1、时间轮的时间点是固定的,如果任务的时间点不在时间轮的时间点上,就会被延迟执行 * 2、任务执行是在这个线程中执行的,如果任务执行时间过长,会影响时间轮的执行 */ class TimerWheel { public: using Task = std::function; /* 构造函数,第一个参数是任务时间点容器最大数量,第二个参数是最低检测时间单位 */ explicit TimerWheel(size_t wheel_size, int interval_ms) : wheel_size_(wheel_size), interval_ms_(interval_ms), wheel_(wheel_size), current_index_(0) {} ~TimerWheel() { Stop(); } /* 开启时间轮 */ void Start() { if (running_) { return; } running_ = true; /* 开启新线程,定时检测队列 */ thread_ = std::thread([this]() { while (running_) { std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms_)); Tick(); } std::cout << "timer oooops!" << std::endl; }); thread_.detach(); } void Stop() { if (!running_) { return; } running_ = false; if (thread_.joinable()) { thread_.join(); } } /* 添加任务函数 */ void AddTask(int timeout_ms, Task task) { std::lock_guard lock(mutex_); /* 计算出需要轮训的次数 */ size_t ticks = timeout_ms / interval_ms_; /* 在当前时间点上加上ticks */ size_t index = (current_index_ + ticks) % wheel_size_; /* 这里是设置在整个时间轮中,该任务在这个时间轮中的分布,以实现循环定时 */ size_t allindex = index; for (size_t i = 1 ; allindex < wheel_size_; i++) { allindex = index * i; if (allindex >= wheel_size_) break; wheel_[allindex].push_back(task); } } private: /* 时间片 */ void Tick() { std::lock_guard lock(mutex_); /* 取出这个时间点的函数,循环执行 */ auto& tasks = wheel_[current_index_]; for (const auto& task : tasks) { task(); } //tasks.clear(); /* 可以循环定时的关键,超过了设置的最大时间点,就会重置 */ current_index_ = (current_index_ + 1) % wheel_size_; } private: size_t wheel_size_; /* 时间轮最大的轮训次数 */ int interval_ms_; /* 每个时间点的间隔秒数 */ std::vector> wheel_; /* 任务队列,同一个时间点可以有多个链表 */ size_t current_index_; /* 现在的时间点 */ bool running_ = false; std::thread thread_; std::mutex mutex_; };