muduo源码分析之定时器TimerQueue

您所在的位置:网站首页 muduo源码剖析 muduo源码分析之定时器TimerQueue

muduo源码分析之定时器TimerQueue

2023-11-12 19:17| 来源: 网络整理| 查看: 265

    (muduo源码系列大多是我在看muduo源码的时候结合网上博客总结的,我尽可能多的是对源码注释) 简介

         Muduo的定时器功能主要由三个class实现,TimerId,Timer,TimerQueue,TimerQueue的接口只有两个addTimer()和cancel(),addTimer()是提供给EventLoop使用的, EventLoop会把它封装成更好用的三个函数:runAt()、runAfter()、runEvery()。

   大体实现

        muduo 定时器封装了 Timer.h 里面保存的是超时时间和回调函数, TimerQueue.h 使用set容器保存多个定时器, 然后在TimerQueue中使用timerfd_create创建一个timerfd句柄, 插入定时器A后先比较A的触发时间和TimerQueue的触发时间, 如果A的触发时间比其小就使用timerfd_settime重置TimerQueue的timerfd的触发时间, TimerQueue中的timerfd的触发时间永远与保存的定时器中触发时间最小的那个相同, 然后timerfd触发可读后, 遍历保存的多个定时器, 看看有没有同时到期的, 有执行回调函数

    TimerQueue的封装是为了让未到期的时间Timer有序的排列起来,这样,能够更具当前时间找到已经到期的Timer也能高效的添加和删除Timer。

所谓的到期与未到期,与当前在当前时间之前表示已经到期,之后则是未到期。为了方便计算,muduo重载了operator 0.0),//如果间隔大于0,就重复 sequence_(s_numCreated_.incrementAndGet()) {}//当前定时器的序列号 //调用回调函数. void run() const { callback_(); } Timestamp expiration() const { return expiration_; }//返回超时时刻 bool repeat() const { return repeat_; }//返回是否重复 int64_t sequence() const { return sequence_; }//返回定时器序号 void restart(Timestamp now);//重新开始 static int64_t numCreated() { return s_numCreated_.get(); }//返回最新的序号值 private: const TimerCallback callback_; // 定时器回调函数 Timestamp expiration_; // 下一次的超时时间戳类 const double interval_; // 超时时间间隔,如果是一次性定时器,该值为0 const bool repeat_; // 是否重复 const int64_t sequence_; // 定时器序号,不会重复 static AtomicInt64 s_numCreated_; // 定时器计数,当前已经创建的定时器数量 }; } } #endif // MUDUO_NET_TIMER_H

 

 

 

Timestamp

时间戳一般用来唯一地标识某一刻的时间,通常是指格林威治时间1970年01月01日00时00分00秒(北京时间1970年01月01日08时00分00秒)起至现在的总毫秒数。

 

Timestamp类的参数有一个常量kMicroSecondsPerSecond表示每秒所对应的微秒数,成员变量microSecondsSinceEpoch_表示到1970-01-01 00:00:00 UTC的微秒数。成员函数包括swap()交换操作,toString()、toFormattedString()将时间转换为string类型或指定格式,valid()判断Timestamp是否有效,invalid()返回一个无效的Timestamp,now()返回当前时间的Timestamp,secondsSinceEpoch()/microSecondsSinceEpoch()返回到1970-01-01 00:00:00 UTC的秒数/微秒数。

Timestamp.h源码(带注释)

#ifndef MUDUO_BASE_TIMESTAMP_H #define MUDUO_BASE_TIMESTAMP_H /*时间戳函数,我的理解是创建一个microSecondsSinceEpoch_为0的结构体, *然后每次通过这个结构体创建子的microSecondsSinceEpoch_为当前时间的结构体,并且子结构体都在microSecondsSinceEpoch_为0的结构体 *中进行运算操作*/ #include #include #include namespace muduo { ///Timestamp /// Time stamp in UTC, in microseconds resolution. /// /// This class is immutable. /// It's recommended to pass it by value, since it's passed in register on x64. /// class Timestamp : public muduo::copyable, public boost::less_than_comparable//继承这个类,对于,=这些运算符号,只需要定义 // 0; }//判断microSecondsSinceEpoch_是否有效,大于0就有效 // for internal usage. int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }//返回microSecondsSinceEpoch_ time_t secondsSinceEpoch() const//返回以秒为单位的microSecondsSinceEpoch_ { return static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond); } /// /// Get time of now. /// static Timestamp now();//创建一个当前的时间的Timestamp结构体 static Timestamp invalid();//创建一个microSecondsSinceEpoch_=0的Timestamp结构体,都是静态函数,可以直接调用 static const int kMicroSecondsPerSecond = 1000 * 1000; private: int64_t microSecondsSinceEpoch_;//表示到1970-01-01 00:00:00 UTC的微秒数。 }; /*放在类外重载,但是普通数据类型可以使用原来的,遇到特定类型才会使用这个*/ inline bool operator 0.0. /// /// Must be thread safe. Usually be called from other threads. // 一定是线程安全的,可以跨线程调用。通常情况下被其它线程调用。 TimerId addTimer(const TimerCallback &cb, Timestamp when, double interval); void cancel(TimerId timerId); private: // FIXME: use unique_ptr instead of raw pointers. // unique_ptr是C++ 11标准的一个独享所有权的智能指针 // 无法得到指向同一对象的两个unique_ptr指针 // 但可以进行移动构造与移动赋值操作,即所有权可以移动到另一个对象(而非拷贝构造) typedef std::pair Entry; typedef std::set TimerList; typedef std::pair ActiveTimer; typedef std::set ActiveTimerSet; //set中存储的是pair类型,那么默认先按照pair的第一个元素排序,如果相同,再按照第二个元素排序。 //所以这两种set都是存放定时器的列表,但是一个根据定时器的到时时间来存储, //一个根据定时器地址来存储,但是存储的定时器都是同一个,目的是为了区分同一到期时间的定时器??? // 以下成员函数只可能在其所属的I/O线程中调用,因而不必加锁。 // 服务器性能杀手之一是锁竞争,所以要尽可能少用锁 void addTimerInLoop(Timer *timer); void cancelInLoop(TimerId timerId); // called when timerfd alarms void handleRead();//timerfdChannel_的读函数 // move out all expired timers // 返回超时的定时器列表 std::vector getExpired(Timestamp now); void reset(const std::vector &expired, Timestamp now); bool insert(Timer *timer); EventLoop *loop_; // 所属EventLoop const int timerfd_; //过一段事件,就筛选一次,看看TimerList中有多少定时器到时间了,就处理一下,但是这样延迟很高,不太理解 Channel timerfdChannel_;//与timefd绑定 // Timer list sorted by expiration TimerList timers_; // timers_是按到期时间排序,也是存放未到时间的定时器 // for cancel() // timers_与activeTimers_保存的是相同的数据 // timers_是按到期时间排序,activeTimers_是按对象地址排序 ActiveTimerSet activeTimers_;//还未到时间的定时器,这里面存放的定时器是和timers_一样的,只是顺序不同 bool callingExpiredTimers_; /* atomic *///是否在处理过期定时器的标志 ActiveTimerSet cancelingTimers_; // 保存的是被取消的定时器 // 用这个列表的作用是,当出现一个循环的计时器被取消时,就要通过reset函数中对 //ActiveTimerSet列表来暂停对这个计时器的重置 }; } } #endif // MUDUO_NET_TIMERQUEUE_H

 

TimerQueue.cc

 

// Copyright 2010, Shuo Chen. All rights reserved. // http://code.google.com/p/muduo/ // // Use of this source code is governed by a BSD-style license // that can be found in the License file. // Author: Shuo Chen (chenshuo at chenshuo dot com) #define __STDC_LIMIT_MACROS #include #include #include #include #include #include #include namespace muduo { namespace net { namespace detail { // 创建定时器 int createTimerfd() { int timerfd = ::timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK | TFD_CLOEXEC);//CLOCK_MONOTONIC参数表明计时器的时间是从系统打开开始计时的 //CLOCK_MONOTONIC表示的是时间类型 if (timerfd < 0) { LOG_SYSFATAL


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3