28.muduo学习笔记之net_TimerQueue.{h&cc}

1. 说明

  1. 用于定时器事件的内部类,用户看不到这个类,这个类维护了一个定时器列表
  2. 这个文件有一个TimerQueue类,使用到了EvenLoop,Timer,TimerId,Channel类
  3. noncopyable
  4. 定时器使用了timerfd_*系列函数,因为这个系列不会产生信号,在多线程程序中处理信号比较麻烦,这个是当做文件描述符处理,当时间到的时候,这个描述符就变为可读,方便用poll等方式实现

2. 变量

  1. 一些类型定义

    • Entry
    • TimerList
    • ActiveTimer
    • ActiveTimerSet
    • TimerQueu需要快速根据当前时间找到已到期的定时器,也要高效的添加和删除Timer,因而可以用二叉搜索树,用map或set
  2. loop_

    • 所属的EventLoop
  3. timerfd_

    • const int类型,定时器的文件描述符
  4. timerfdChannel_

    • timerfd的监听通道
  5. timers_

    • 按照到期时间排序的Timer*列表
  6. activeTimers_

    • 和timers_保存的相同的数据,activeTimers_是按照对象地址排序
  7. callingExpiredTimers_

    • bool类型,是否处于调用处理超时定时器当中,bool类型在Linux下自身就是原子操作
    • 在handleRead()函数中使用
  8. cancelingTimers_

    • 保存的是被取消的定时器

3. 函数

1. 公有方法

  1. 构造
    • 调用了timerfdChannel_.setReadCallback(std::bind(&TimerQueue::handleRead, this)); 意思是当定时器通道可读时,回调handleRead()这个函数
    • 调用timerfd_create()生成文件描述符给timerfd赋值
    • 调用timerfdChannel_.enableReading();,会调用EventLoop的updateChannel(),从而调用Poller的updateChannel(),把该通道加入poller进行关注
  2. 析构
    • 释放资源
  3. addTimer(TimerCallback cb,Timestamp when,double interval);
    • 添加一个定时器,返回一个TimerId
    • 实际是通过loop_调用本类的私有方法addTimerInLoop
    • 必须是线程安全的,可以跨线程调用
  4. cancel(TimerId timerId);
    • 传一个TimerId,取消这个定时器
    • 实际是通过loop_调用本类的私有方法cancelInLoop
    • 以上两个函数并不会直接让用户调用,而是通过EventLoop中使用

2. 私有方法

  1. addTimerInLoop(Timer* timer);

    • 调用loop_->assertInLoopThread();作用是断言处于IO线程中
    • 接着调用insert()插入定时器,但插入的有可能使得最早到期的定时器发生改变,也就是说插入的定时器时间比最早的还早,这时候就会返回true,从而接着调用resetTimerfd(),重置定时器的超时时刻,也就是重新设置最早的定时器,让最早的定时器先触发
  2. cancelInLoop(TimerId timerId);

    • 如果当前定时器没有处于正在调用处理中,即callingExpiredTimers_为false,这时直接删除这个定时器
    • 如果在timers_列表中找不到timerId并且处于callingExpiredTimers_中,可能正在处理这个定时器的回调函数,详见下面handleRead()方法,这时候把该定时器加入到cancelingTimers_ 中,这样的话,在handleRead()处理完后调用reset()方法,就会把这个定时器删掉,不再repeat()
    • 这个我看懂后,虽然貌似不是很难想象的操作,但还是奉上一句:大佬大佬!
  3. handleRead();

    • 先调用getExpired()获得该时刻之前所有的定时器列表,即超时定时器列表
    • 设置callingExpiredTimers_为true,即正在处理这些定时器回调,在这个函数结束之前再变为false
    • 依次调用这些定时器的run()方法,实际上调用了回调函数
    • 最后清除取消列表,调用reset,重启需要重复调用的定时器
  4. getExpired(Timestamp now);

    • 先调用Set的lower_bound(x)函数,作用是返回第一个值>=x的元素的迭代器iterator,在这里的作用是返回第一个未到期的Timer的迭代器
    • 这里的sentry,后一个参数是赋值为最大值,这样比较的时候,如果第一个值相等,第二个值就必然比当前小,调用lower_bound()就满足条件,reinterpret_cast<>用在任意指针或引用类型之间的转换
    • 把到期的定时器放到一个新的vector里返回,并从timers_和activeTimers_中删除这些定时器
    • 这里直接返回vector,并不会调用拷贝构造,因为会有rvo优化(return value optimization)
  5. reset(const std::vector& expired, Timestamp now);

    • 重启expired中需要重复的vector,如果需要重复并且没有被取消,重新设定超时时间,插入定时器列表,最后再重新设定最早的定时器
  6. insert(Timer* timer);

    • 在timers_和activeTimers_中插入这个Timer*对象
    • 判断这个timer是否是最早的定时器,返回bool类型,在上面的addTimerInLoop()说明中有说

4. 全局方法

  1. createTimerfd()

    • 调用timerfd_create()生成文件描述符返回
  2. howMuchTimeFromNow(Timestamp when)

    • 如名,when比现在晚,计算两个的时间差,返回一个timespec结构体,计算超时时刻与当前时间的时间差
  3. readTimerfd(int timerfd, Timestamp now)

    • 调用read()读timerfd文件描述符的内容,只读不做其他操作,顺便写日志
    • 就是调用read(),清除定时器,避免一直触发
  4. resetTimerfd(int timerfd, Timestamp expiration)

    • 调用timerfd_settime(),重置定时器的超时时刻
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值