muduo网络库学习(三)定时器TimerQueue的设计

本文详细介绍了muduo网络库中TimerQueue的设计选择,包括为何选用gettimeofday获取时间以及为什么使用timerfd_*系列函数处理定时任务。gettimeofday因精度和效率而被选中,而timerfd_create则因避免信号处理和便于融入事件循环而受到青睐。TimerQueue通过红黑树组织超时任务,并通过文件描述符进行监听,当超时触发时,回调用户提供的函数。文章还讨论了添加、删除定时任务的过程以及C++中的一些相关技术应用。
摘要由CSDN通过智能技术生成

Linux下用于获取当前时间的函数有

  • time(2) / time_t (秒)
  • ftime(3) / struct timeb (毫秒)
  • gettimeofday(2) / struct timeval (微秒)
  • clock_gettime(2) / struct timespec (纳秒)

定时函数,用于让程序等待一段时间或安排计划任务

  • sleep(3)
  • alarm(2)
  • usleep(3)
  • nanosleep(2)
  • clock_nanosleep(2)
  • getitimer(2) / setitimer(2)
  • timer_create(2) / timer_settime(2) / timer_gettime(2) / timer_delete(2)
  • timerfd_create(2) / timerfd_gettime(2) / timerfd_settime(2)

muduo的取舍是

  • (计时)只使用gettimeofday(2)来获取当前时间
  • (定时)只使用timerfd_*系列函数来处理定时任务

gettimeofday入选的原因

  1. time的精度太低,ftime已被废弃,clock_gettime精度最高,但是系统调用开销比gettimeofday大
  2. 在x86-64平台上,gettimeofday不是系统调用,而是在用户态完成的,没有上下文切换和陷入内核的开销
  3. gettimeofday的分辨率在微秒级,足以满足日常计时的需要

timerfd*_入选的原因

  1. sleep / alarm / usleep在实现时有可能使用了SIGALRM信号,多线程程序中尽量避免使用信号,因为处理起来比较麻烦(信号通知进程,所有线程都将接收到这个信号,谁处理好)。另外,如果网络库定义了信号处理函数,用户代码(main函数等使用库的程序)也定义了信号处理函数,这不就冲突了,该调用哪个好
  2. nanosleep / clock_nanosleep是线程安全的,但是会让当前线程挂起等待一段时间,这会导致线程失去响应
  3. gettimer和timer_create也是用信号来传递超时信息
  4. timerfd_create把时间变成了一个文件描述符,该描述符在定时器超时的那一刻变为可读,可以很方便的融入到select/poll/epoll中,统一事件源
  5. select/poll/epoll可以设置timeout来实现超时,但是poll/epoll的精度只有毫秒,远低于timerfd_settime

                        ---------------------------------------摘自《Linux多线程服务器编程》
    

gettimeofday

#include <sys/time.h>
int gettimeofday(struct timeval* tv, struct timezone* tz);
/* 
 * 返回后将目前的时间存放在tv中,把时区信息存放在tz中 
 * tz和tz都可以为NULL
 * 执行成功返回0,失败返回-1
 */

struct timeval{
    long tv_sec;   /* 秒 */
    long tv_usec;  /* 微秒 */
};

struct timeval timer;
gettimeofday(&timer, NULL);

timerfd_*系列函数

#include <sys/timerfd.h>
int timerfd_create(int clockid, int flags);
/*
 * 创建一个用于定时器的文件描述符
 * clockid可以为
 *     CLOCK_REALTIME(系统实时时间,可能会被用户手动更改)
 *     CLOCK_MONOTONIC(从系统启动那一刻开始计时的时间,无法被用户更改)
 * flags可以为
 *     TFD_NONBLOCK(非阻塞)
 *     TFD_CLOEXEC(调用exec时自动close)
 */


int timerfd_settime(int fd, int flag, 
                    const struct itimerspec* new_value,
                    const struct itimerspec* old_value);
/*
 * 用于设置timerfd的超时时间
 * fd, 通过timerfd_create返回的文件描述符
 * flag, 0代表相对时间,TFD_TIMER_ABSTIME代表绝对时间
 * new_value, 新设置的超时时间
 * old_value, 以前设置的超时时间,值-结果参数
 *  
 * struct timespec{
 *     time_t tv_sec;
 *     time_t tv_nsec;
 * };
 * 
 * struct itimerspec{
 *     struct timespec it_interval;
 *     struct timespce it_value;
 * }; 
 *  
 * it_value表示首次超时时间
 * it_interval表示后续周期性超时时间
 * it_interval不为0表示是周期性超时任务
 * it_interval和it_value同时为0表示取消定时任务
 */


int timerfd_gettime(int fd, struct itimerspec *cur_value);
/*
 * 返回距离下次超时还剩多长时间,保存在cur_value中
 * 如果调用时定时器已经到期,同时定时器设置了周期性任务(it_interval不为0)
 * 那么调用此函数之后定时器重新开始计时,超时时间是it_interval的值
 */

Timestamp类用于保存超时时间,类中只有一个成员变量,保存当前UTC时间,即从Unix Epoch(1970-01-01 00:00:00)到现在的微秒数
Timer类是一个超时任务,保存超时时间Timestamp,回调函数,以及记录自己是否是周期性计时任务,回

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值