在服务端开发,特别是游戏服务端开发过程中,定时器必不可少,而且用得非常多。看网上有的人是直接在线程的loop中每次都对定时器进行检测,例如:
- void loop() {
- while (running) {
- // Do same work
- Timer timer = getTopTimer();
- while (timer->time < now()) {
- timer->dg();
- timer = getTopTimer();
- }
- // Do same work
- }
- }
这样做每次循环都会检测定时器,浪费CPU,而且定时器精确性不高。
现在Linux下有了新的实现定时器的方案,使用timerfd,它将定时器抽象成文件描述符,可以结合epoll一起使用,非常方便,但Linux内核版本必须大于等于2.6.25。
下面是我最近封装的定时器,性能不会随着定时器的增加而降低。
- class Timer : public boost::noncopyable, public boost::enable_shared_from_this<Timer> {
- friend class TimerManager;
- public:
- DEFINE_PTR(Timer);
- typedef boost::function<void (Timer::ptr)> EntryPoint;
- private:
- Timer(microsec_t next, microsec_t interval, EntryPoint entryPoint, const boost::weak_ptr<TimerManager> &timerMgr);
- // Constructor for dummy object
- Timer(microsec_t next)
- : m_next(next)
- , m_interval(0)
- {}
- public:
- bool cancel();
- bool refresh();
- //bool reset(microsec_t interval, bool fromNow = false);
- private:
- microsec_t m_next;
- microsec_t m_interval; // 如果m_interval等于0,代表定时器只触发一次
- EntryPoint m_entryPoint;
- boost::weak_ptr<TimerManager> m_timerMgr;
- };
- class TimerManager : boost::noncopyable, public boost::enable_shared_from_this<TimerManager> {
- friend class Timer;
- public:
- DEFINE_PTR(TimerManager);
- public:
- TimerManager(IoScheduler &scheduler);
- virtual ~TimerManager();
- void stop();
- Timer::ptr registerTimerAt(microsec_t next, microsec_t interval, Timer::EntryPoint entryPoint);
- Timer::ptr registerTimerAfter(microsec_t next, microsec_t interval, Timer::EntryPoint entryPoint);
- bool hasTimer();
- static microsec_t now();
- protected:
- void run();
- void processTimers();
- void setTimer(const microsec_t &next);
- private:
- struct TimerLess {
- bool operator() (const Timer::ptr &lhs, const Timer::ptr &rhs) const;
- };
- private:
- typedef std::set<Timer::ptr, TimerLess> Timers;
- ThreadMutex m_mutex;
- IoScheduler &m_scheduler;
- int m_timerFd; // timerfd
- io_event_t m_registerEvent;
- Timers m_timers;
- bool m_running;
- };
- TimerManager::TimerManager(IoScheduler &scheduler)
- : m_scheduler(scheduler)
- , m_running(false)
- {
- FUNCTION_TRACKER();
- m_timerFd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);
- if (m_timerFd == -1) {
- G_THROW_EXCEPTION_FROM_LAST_ERROR_API("timerfd_create");
- }
- m_scheduler.registerEvent(m_timerFd, IoScheduler::READ);
- m_registerEvent = IoScheduler::READ;
- m_running = true;
- m_scheduler.schedule(boost::bind(&TimerManager::run, this), true);
- }
- /*virtual*/ TimerManager::~TimerManager() {
- FUNCTION_TRACKER();
- close(m_timerFd);
- }
- void TimerManager::stop() {
- FUNCTION_TRACKER();
- ThreadMutex::Guard guard(m_mutex);
- if (m_running) {
- m_running = false;
- G_ASSERT(m_registerEvent != IoScheduler::NONE);
- m_scheduler.cancelWait(m_timerFd, IoScheduler::READ);
- m_scheduler.unregisterEvent(m_timerFd, (IoScheduler::Event)m_registerEvent);
- m_registerEvent = IoScheduler::NONE;
- }
- }
- Timer::ptr TimerManager::registerTimerAt(microsec_t next, microsec_t interval,
- Timer::EntryPoint entryPoint)
- {
- FUNCTION_TRACKER();
- Timer::ptr timer(new Timer(next, interval, entryPoint, shared_from_this()));
- ThreadMutex::Guard guard(m_mutex);
- Timers::iterator it = m_timers.insert(timer).first;
- bool firstTimer = (it == m_timers.begin());
- if (firstTimer) {
- setTimer(timer->m_next);
- }
- return timer;
- }
- Timer::ptr TimerManager::registerTimerAfter(microsec_t next,
- microsec_t interval, Timer::EntryPoint entryPoint)
- {
- FUNCTION_TRACKER();
- Timer::ptr timer(new Timer(now() + next, interval, entryPoint, shared_from_this()));
- ThreadMutex::Guard guard(m_mutex);
- Timers::iterator it = m_timers.insert(timer).first;
- bool firstTimer = (it == m_timers.begin());
- if (firstTimer) {
- setTimer(timer->m_next);
- }
- return timer;
- }
- bool TimerManager::hasTimer() {
- FUNCTION_TRACKER();
- ThreadMutex::Guard guard(m_mutex);
- return !m_timers.empty();
- }
- /*static*/ microsec_t TimerManager::now() {
- FUNCTION_TRACKER();
- return getCurrentMicroSecond();
- }
- void TimerManager::run() {
- FUNCTION_TRACKER();
- G_LOG_INFO(g_logger) << "Timer manager fiber run";
- uint64_t data;
- while (m_running) {
- m_scheduler.asyncWait(m_timerFd, IoScheduler::READ);
- if (read(m_timerFd, &data, sizeof(data)) == -1) {
- if (errno != EAGAIN) {
- G_THROW_EXCEPTION_FROM_LAST_ERROR_API("read");
- }
- continue;
- }
- G_ASSERT(data == 1);
- processTimers();
- }
- G_LOG_INFO(g_logger) << "Timer manager fiber exit";
- }
- void TimerManager::processTimers() {
- FUNCTION_TRACKER();
- ThreadMutex::Guard guard(m_mutex);
- microsec_t nowTime = now();
- Timers::iterator it = m_timers.begin();
- while (it != m_timers.end()) {
- Timer::ptr timer = *it;
- if (timer->m_next > nowTime) {
- setTimer(timer->m_next);
- return;
- }
- m_timers.erase(it);
- G_ASSERT(timer->m_entryPoint);
- if (timer->m_interval != 0) {
- timer->m_next = nowTime + timer->m_interval;
- m_timers.insert(timer);
- }
- m_scheduler.schedule(boost::bind(timer->m_entryPoint, timer), true);
- it = m_timers.begin();
- }
- return;
- }
- void TimerManager::setTimer(const microsec_t &next) {
- FUNCTION_TRACKER();
- itimerspec val;
- val.it_value.tv_sec = next / MICROSECOND_PER_SECOND;
- val.it_value.tv_nsec = (next % MICROSECOND_PER_SECOND) * 1000;
- val.it_interval.tv_sec = 0;
- val.it_interval.tv_nsec = 0;
- if (timerfd_settime(m_timerFd, TFD_TIMER_ABSTIME, &val, NULL) == -1) {
- G_THROW_EXCEPTION_FROM_LAST_ERROR_API("timerfd_settime");
- }
- }
- bool TimerManager::TimerLess::operator() (const Timer::ptr &lhs, const Timer::ptr &rhs) const {
- if (!lhs) {
- return true;
- } else if (!rhs) {
- return false;
- }
- if (lhs->m_next < rhs->m_next) {
- return true;
- } else if (lhs->m_next > rhs->m_next) {
- return false;
- } else {
- return lhs.get() < rhs.get(); // 保证TimerManager中能包含两个m_next一样的Timer
- }
- }