在服務端開發,特別是遊戲服務端開發過程當中,定時器必不可少,並且用得很是多。看網上有的人是直接在線程的loop中每次都對定時器進行檢測,例如:併發
- void loop() {
- while (running) {
- // Do same work
- Timer timer = getTopTimer();
- while (timer->time < now()) {
- timer->dg();
- timer = getTopTimer();
- }
- // Do same work
- }
- }
這樣作每次循環都會檢測定時器,浪費CPU,並且定時器精確性不高。async
如今Linux下有了新的實現定時器的方案,使用timerfd,它將定時器抽象成文件描述符,能夠結合epoll一塊兒使用,很是方便,但Linux內核版本必須大於等於2.6.25。高併發
下面是我最近封裝的定時器,性能不會隨着定時器的增長而下降。oop
- 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
- }
- }