muduo网络库定时器的实现

本文详细介绍了基于timerfd的定时器的设计原理与实现方法,包括timerfd_create、timerfd_settime等关键API的使用,以及如何通过TimerQueue管理多个定时器。此外,还探讨了定时器在实际网络编程中的应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


一:函数介绍

常见的与时间相关的函数有:sleep,alarm,usleep,nanosleep,clock_nanosleep,gettimer/settitimer,timer_create/timer_settime/timer_gettime/timer_delete,还有muduo使用的timerfd_create/timerfd_gettime/timerfd_settime函数。

为什么选择timerfd_*函数呢?

>>>sleep/alarm/usleep在实现时有可能用了信号SIGALRM,在多线程程序中处理信号是个相当麻烦的事情,应该尽量避免。

>>>nanosleep和clock_nanosleep是线程安全的,但是在非阻塞网络编程中,绝对不能用让线程挂起的方式来等待一段时间。程序会失去响应,正确的做法是注册一个时间回调函数。

>>>getitimer和timer_create也是用信号来deliver超时,在多线程程序中也会有麻烦。

>>>timer_create可以指定信号的接收方是进程还是线程,算是一个进步,不过在信号处理函数(signal handler)中能做的事情实在是很受限。

>>>timerfd_create把时间变成了一个文件描述符,该“文件”在定时器超时的那一刻变得可读,这样就能很方便的融入到select/poll框架中,用统一的方式来处理I/O和超时事件,这正是Reactor模式的长处。实际上如果非要处理信号的话,使用signalfd将信号转变成文件描述符处理。

比较:libevent定时器采用本地unix域通信,当时间最小堆中超时事件发生后,写一个字节给本地socket,使得描述符可读,epoll检测到并处理该事件。


首先看timerfd_create()函数:

 int timerfd_create(int clockid, int flags);
1.参数1的clockid是指,CLOCK_REALTIME or CLOCK_MONOTONIC时间。前者是我们平时所看到的时间,后者是石英钟时间也就是晶振决定的时间。所以假如我们制订了3分钟的定时,如果我们直接修改时间到三分钟后,那么前者会触发,而后者不会触发,它是晶振决定的相对时间,不取决于外部更改。所以一般定时器采用后者

2.参数2有两个选项TFD_NONBLOCK和TFD_CLOEXEC,这个就不用解释了。


第二个函数是timerfd_settime()函数

  int timerfd_settime(int fd, int flags,
                           const struct itimerspec *new_value,
                           struct itimerspec *old_value);
1.参数1是文件描述符。

2.参数2是参加timerfd_create()函数。

3.参数3是新值,itermerspec结构体有两个字段,解释见下面。

4.参数4是旧值,结构体见下面。

           struct timespec {
               time_t tv_sec;                /* Seconds */
               long   tv_nsec;               /* Nanoseconds */
           };


           struct itimerspec {
               struct timespec it_interval;  /* Interval for periodic timer */   //定时间隔,如果只发生一次,可设置为0
               struct timespec it_value;     /* Initial expiration */  //最初的过期值
           };


       new_value.it_value  specifies  the  initial  expiration  of the timer, in seconds and nanoseconds.  Setting either field of

       new_value.it_value to a nonzero value arms the timer.  Setting both fields of new_value.it_value to zero disarms the timer.


二:定时器

为什么网络编程中需要定时器呢?

在开发Linux网络程序时,通常需要维护多个定时器,如维护客户端心跳时间、检查多个数据包的超时重传等。如果采用Linux的SIGALARM信号实现,则会带来较大的系统开销,且不便于管理。

Muduo 的 TimerQueue 采用了最简单的实现(链表)来管理定时器,它的效率比不上常见的 binary heap 的做法,如果程序中大量(10 个以上)使用重复触发的定时器,或许值得考虑改用更高级的实现。由于目前还没有在一个程序里用过这么多定时器,暂时也不需要优化 TimerQueue。

mudo的定时器由三个类实现,TimerId,Timer,TimerQueue,用户只能看到第一个类,其它两个类都是内部实现细节。

(1)TimerId被设计用来取消Timer的,它的结构很简单,只有一个Timer指针和其序列号。其中还声明了TimerQueue为其友元,可以操作其私有数据。

它的实现如下:

class TimerId : public muduo::copyable
{
 public:
  TimerId()
    : timer_(NUL
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值