实现定时器-1

281 阅读2分钟

这是我参与8月更文挑战的第16天,活动详情查看:8月更文挑战

重数据结构来划分,常见的定时器设计一般有 排序链表、红黑树和时间轮、最小堆等。

对于第一种排序链表,就是每次插入任务的时候,按照时间的先后进行插入定时任务,从而达到实现定时执行函数的功能。

而第二种,红黑树,可以在 nginx 中看到相关的实现,利用红黑树的时间优势,可以做到高精度低延迟的定时器。

第三种可以在Linux 中刚看到相关实现,其本质是利用循环链表,但实现起来其实要更复杂一点。属于优点缺点都很明显的设计。

从时间方面来看,定时器也可以分成两种,一种是绝对时间,一种是相对时间。往往在硬件环境和软件环境中,由于时间精度可以自己调整,所以相对时间的选择比较多,而且选择绝对时间,也就是利用系统时间的实现,还需要处理系统发生篡改时间的情况,处理起来较为复杂。

执行到期任务的时候也有几种选择:

依赖事件循环,例如 libhv:github.com/ithewei/lib…

static int hloop_process_timers(hloop_t* loop) { |                                                                                              |
| ------------------------------------------------ | -------------------------------------------------------------------------------------------- |
|                                                  | int ntimers = 0;                                                                             |
|                                                  | htimer_t* timer = NULL;                                                                      |
|                                                  | uint64_t now_hrtime = hloop_now_hrtime(loop);                                                |
|                                                  | while (loop->timers.root) {                                                                  |
|                                                  | // NOTE: root of minheap has min timeout.                                                    |
|                                                  | timer = TIMER_ENTRY(loop->timers.root);                                                      |
|                                                  | if (timer->next_timeout > now_hrtime) {                                                      |
|                                                  | break;                                                                                       |
|                                                  | }                                                                                            |
|                                                  | if (timer->repeat != INFINITE) {                                                             |
|                                                  | --timer->repeat;                                                                             |
|                                                  | }                                                                                            |
|                                                  | if (timer->repeat == 0) {                                                                    |
|                                                  | // NOTE: Just mark it as destroy and remove from heap.                                       |
|                                                  | // Real deletion occurs after hloop_process_pendings.                                        |
|                                                  | __htimer_del(timer);                                                                         |
|                                                  | }                                                                                            |
|                                                  | else {                                                                                       |
|                                                  | // NOTE: calc next timeout, then re-insert heap.                                             |
|                                                  | heap_dequeue(&loop->timers);                                                                 |
|                                                  | if (timer->event_type == HEVENT_TYPE_TIMEOUT) {                                              |
|                                                  | while (timer->next_timeout <= now_hrtime) {                                                  |
|                                                  | timer->next_timeout += (uint64_t)((htimeout_t*)timer)->timeout * 1000;                       |
|                                                  | }                                                                                            |
|                                                  | }                                                                                            |
|                                                  | else if (timer->event_type == HEVENT_TYPE_PERIOD) {                                          |
|                                                  | hperiod_t* period = (hperiod_t*)timer;                                                       |
|                                                  | timer->next_timeout = (uint64_t)cron_next_timeout(period->minute, period->hour, period->day, |
|                                                  | period->week, period->month) * 1000000;                                                      |
|                                                  | }                                                                                            |
|                                                  | heap_insert(&loop->timers, &timer->node);                                                    |
|                                                  | }                                                                                            |
|                                                  | EVENT_PENDING(timer);                                                                        |
|                                                  | ++ntimers;                                                                                   |
|                                                  | }                                                                                            |
|                                                  | return ntimers;                                                                              |
|                                                  | }

不依赖事件循环,列如,用一个列表记录定时器,每当处理函数被调用时从列表取出已超时的定时器,然后调用这些定时器的回调函数。这种情况就需要将定时器中,一般设置为static 的变量重外部导入。