[高并发引擎]定时器模块

本文来源于libGod官网http://www.libgod.com,转载请注明来源

在服务端开发,特别是游戏服务端开发过程中,定时器必不可少,而且用得非常多。看网上有的人是直接在线程的loop中每次都对定时器进行检测,例如:

  1. void loop() {  
  2.     while (running) {  
  3.         // Do same work  
  4.         Timer timer = getTopTimer();  
  5.         while (timer->time < now()) {  
  6.             timer->dg();  
  7.             timer = getTopTimer();  
  8.         }  
  9.         // Do same work  
  10.     }  
  11. }  

这样做每次循环都会检测定时器,浪费CPU,而且定时器精确性不高。

现在Linux下有了新的实现定时器的方案,使用timerfd,它将定时器抽象成文件描述符,可以结合epoll一起使用,非常方便,但Linux内核版本必须大于等于2.6.25。

下面是我最近封装的定时器,性能不会随着定时器的增加而降低。

 

  1. class Timer : public boost::noncopyable, public boost::enable_shared_from_this<Timer> {   
  2. friend class TimerManager;   
  3. public:   
  4.     DEFINE_PTR(Timer);   
  5.     typedef boost::function<void (Timer::ptr)> EntryPoint;   
  6.   
  7. private:   
  8.     Timer(microsec_t next, microsec_t interval, EntryPoint entryPoint, const boost::weak_ptr<TimerManager> &timerMgr);   
  9.   
  10.     // Constructor for dummy object   
  11.     Timer(microsec_t next)   
  12.         : m_next(next)   
  13.         , m_interval(0)   
  14.     {}   
  15.   
  16. public:   
  17.     bool cancel();   
  18.   
  19.     bool refresh();   
  20.   
  21.     //bool reset(microsec_t interval, bool fromNow = false);   
  22.   
  23. private:   
  24.     microsec_t m_next;   
  25.     microsec_t m_interval; // 如果m_interval等于0,代表定时器只触发一次   
  26.     EntryPoint m_entryPoint;   
  27.     boost::weak_ptr<TimerManager> m_timerMgr;   
  28. };  

 

  1. class TimerManager : boost::noncopyable, public boost::enable_shared_from_this<TimerManager> {   
  2. friend class Timer;   
  3. public:   
  4.     DEFINE_PTR(TimerManager);   
  5.   
  6. public:   
  7.     TimerManager(IoScheduler &scheduler);   
  8.   
  9.     virtual ~TimerManager();   
  10.   
  11.     void stop();   
  12.   
  13.     Timer::ptr registerTimerAt(microsec_t next, microsec_t interval, Timer::EntryPoint entryPoint);   
  14.   
  15.     Timer::ptr registerTimerAfter(microsec_t next, microsec_t interval, Timer::EntryPoint entryPoint);   
  16.   
  17.     bool hasTimer();   
  18.   
  19.     static microsec_t now();   
  20.   
  21. protected:   
  22.     void run();   
  23.     void processTimers();   
  24.     void setTimer(const microsec_t &next);   
  25.   
  26. private:   
  27.     struct TimerLess {   
  28.         bool operator() (const Timer::ptr &lhs, const Timer::ptr &rhs) const;   
  29.     };   
  30.   
  31. private:   
  32.     typedef std::set<Timer::ptr, TimerLess> Timers;   
  33.     ThreadMutex m_mutex;   
  34.     IoScheduler &m_scheduler;   
  35.     int m_timerFd; // timerfd   
  36.     io_event_t m_registerEvent;   
  37.     Timers m_timers;   
  38.     bool m_running;   
  39. };  

 

  1. TimerManager::TimerManager(IoScheduler &scheduler)   
  2.     : m_scheduler(scheduler)   
  3.     , m_running(false)   
  4. {   
  5.     FUNCTION_TRACKER();   
  6.     m_timerFd = timerfd_create(CLOCK_REALTIME, TFD_NONBLOCK);   
  7.     if (m_timerFd == -1) {   
  8.         G_THROW_EXCEPTION_FROM_LAST_ERROR_API("timerfd_create");   
  9.     }   
  10.     m_scheduler.registerEvent(m_timerFd, IoScheduler::READ);   
  11.     m_registerEvent = IoScheduler::READ;   
  12.     m_running = true;   
  13.     m_scheduler.schedule(boost::bind(&TimerManager::run, this), true);   
  14. }   
  15.   
  16. /*virtual*/ TimerManager::~TimerManager() {   
  17.     FUNCTION_TRACKER();   
  18.     close(m_timerFd);   
  19. }   
  20.   
  21. void TimerManager::stop() {   
  22.     FUNCTION_TRACKER();   
  23.     ThreadMutex::Guard guard(m_mutex);   
  24.     if (m_running) {   
  25.         m_running = false;   
  26.         G_ASSERT(m_registerEvent != IoScheduler::NONE);   
  27.         m_scheduler.cancelWait(m_timerFd, IoScheduler::READ);   
  28.   
  29.         m_scheduler.unregisterEvent(m_timerFd, (IoScheduler::Event)m_registerEvent);   
  30.         m_registerEvent = IoScheduler::NONE;   
  31.     }   
  32. }   
  33.   
  34. Timer::ptr TimerManager::registerTimerAt(microsec_t next, microsec_t interval,   
  35.     Timer::EntryPoint entryPoint)   
  36. {   
  37.     FUNCTION_TRACKER();   
  38.     Timer::ptr timer(new Timer(next, interval, entryPoint, shared_from_this()));   
  39.     ThreadMutex::Guard guard(m_mutex);   
  40.     Timers::iterator it = m_timers.insert(timer).first;   
  41.     bool firstTimer = (it == m_timers.begin());   
  42.     if (firstTimer) {   
  43.         setTimer(timer->m_next);   
  44.     }   
  45.     return timer;   
  46. }   
  47.   
  48. Timer::ptr TimerManager::registerTimerAfter(microsec_t next,   
  49.     microsec_t interval, Timer::EntryPoint entryPoint)   
  50. {   
  51.     FUNCTION_TRACKER();   
  52.     Timer::ptr timer(new Timer(now() + next, interval, entryPoint, shared_from_this()));   
  53.     ThreadMutex::Guard guard(m_mutex);   
  54.     Timers::iterator it = m_timers.insert(timer).first;   
  55.     bool firstTimer = (it == m_timers.begin());   
  56.     if (firstTimer) {   
  57.         setTimer(timer->m_next);   
  58.     }   
  59.     return timer;   
  60. }   
  61.   
  62. bool TimerManager::hasTimer() {   
  63.     FUNCTION_TRACKER();   
  64.     ThreadMutex::Guard guard(m_mutex);   
  65.     return !m_timers.empty();   
  66. }   
  67.   
  68. /*static*/ microsec_t TimerManager::now() {   
  69.     FUNCTION_TRACKER();   
  70.     return getCurrentMicroSecond();   
  71. }   
  72.   
  73. void TimerManager::run() {   
  74.     FUNCTION_TRACKER();   
  75.     G_LOG_INFO(g_logger) << "Timer manager fiber run";   
  76.     uint64_t data;   
  77.     while (m_running) {   
  78.         m_scheduler.asyncWait(m_timerFd, IoScheduler::READ);   
  79.         if (read(m_timerFd, &data, sizeof(data)) == -1) {   
  80.             if (errno != EAGAIN) {   
  81.                 G_THROW_EXCEPTION_FROM_LAST_ERROR_API("read");   
  82.             }   
  83.             continue;   
  84.         }   
  85.         G_ASSERT(data == 1);   
  86.         processTimers();   
  87.     }   
  88.     G_LOG_INFO(g_logger) << "Timer manager fiber exit";   
  89. }   
  90. void TimerManager::processTimers() {   
  91.     FUNCTION_TRACKER();   
  92.     ThreadMutex::Guard guard(m_mutex);   
  93.     microsec_t nowTime = now();   
  94.     Timers::iterator it = m_timers.begin();   
  95.     while (it != m_timers.end()) {   
  96.         Timer::ptr timer = *it;   
  97.         if (timer->m_next > nowTime) {   
  98.             setTimer(timer->m_next);   
  99.             return;   
  100.         }   
  101.         m_timers.erase(it);   
  102.         G_ASSERT(timer->m_entryPoint);   
  103.         if (timer->m_interval != 0) {   
  104.             timer->m_next = nowTime + timer->m_interval;   
  105.             m_timers.insert(timer);   
  106.         }   
  107.         m_scheduler.schedule(boost::bind(timer->m_entryPoint, timer), true);   
  108.         it = m_timers.begin();   
  109.     }   
  110.     return;   
  111. }   
  112.   
  113. void TimerManager::setTimer(const microsec_t &next) {   
  114.     FUNCTION_TRACKER();   
  115.     itimerspec val;   
  116.     val.it_value.tv_sec = next / MICROSECOND_PER_SECOND;   
  117.     val.it_value.tv_nsec = (next % MICROSECOND_PER_SECOND) * 1000;   
  118.     val.it_interval.tv_sec = 0;   
  119.     val.it_interval.tv_nsec = 0;   
  120.     if (timerfd_settime(m_timerFd, TFD_TIMER_ABSTIME, &val, NULL) == -1) {   
  121.         G_THROW_EXCEPTION_FROM_LAST_ERROR_API("timerfd_settime");   
  122.     }   
  123. }   
  124.   
  125. bool TimerManager::TimerLess::operator() (const Timer::ptr &lhs, const Timer::ptr &rhs) const {   
  126.     if (!lhs) {   
  127.         return true;   
  128.     } else if (!rhs) {   
  129.         return false;   
  130.     }   
  131.     if (lhs->m_next < rhs->m_next) {   
  132.         return true;   
  133.     } else if (lhs->m_next > rhs->m_next) {   
  134.         return false;   
  135.     } else {   
  136.         return lhs.get() < rhs.get(); // 保证TimerManager中能包含两个m_next一样的Timer   
  137.     }   
  138. }  

你可能感兴趣的:([高并发引擎]定时器模块)