Libev学习笔记4

这一节分析Libev的定时器部分。对定时器的使用主要有两个函数:

ev_timer_init (&timeout_watcher, timeout_cb, 5.5, 0.);
ev_timer_start (loop, &timeout_watcher);

和ev_io类型的watcher类似,timeout_watcher是一个类型为ev_timer的watcher,上面的ev_timer_init函数将它设置为5.5秒之后调用回调函数timeout_cb,最后一个参数0表示定时器不重复超时,执行完一次回调函数后就停止计时,如果最后一个参数为非0,那么回调函数第一次执行完之后,每个指定秒后回调函数会被重复执行。将ev_timer结构体展开:

typedef struct ev_timer
{
  int active; /* private */            \     /* 非0表示watcher为激活状态,是periodics或timers数组的下标 */
  int pending; /* private */            \    /* 非0表示watcher中有事件被触发,是pendings数组的下标 */
  EV_DECL_PRIORITY /* private */        \    /* watcher的优先级 */
  EV_COMMON /* rw */                \        /* void *data; */
  EV_CB_DECLARE (type) /* private */         /* void (*cb)(EV_P_ struct type *w, int revents); */
  ev_tstamp at;

  ev_tstamp repeat; /* rw */
} ev_timer;

其中at成员对应倒数第二个参数,表示多少秒后第一次触发超时;repeat成员对应最后一个参数,表示第一次超时触发之后每个多少秒重复触发。ev_timer_init函数实质上就是设置上面这些成员。ev_timer_start主要工作是将timer放入最小堆中,由最小堆统一管理所有的timer,代码如下:

/* 计算定时器的绝对触发事件并放入堆中 */
void noinline
ev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
  if (expect_false (ev_is_active (w)))
    return;

  ev_at (w) += mn_now;    /* 从现在开始经过at秒后触发 */++timercnt;
  ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);

  /* 将定时器w放入timers堆中 */
  array_needsize (ANHE, timers, timermax, ev_active (w) + 1, EMPTY2);
  ANHE_w (timers [ev_active (w)]) = (WT)w;
  ANHE_at_cache (timers [ev_active (w)]);

  /* 更新heap */
  upheap (timers, ev_active (w));
}

代码首先根据当前时间计算timer超时时刻的绝对时间,然后增加loop管理的timer个数timercnt,修改一些标志变量,最后把timer放入最小堆timers中,timers使用数组实现的一个最小堆,堆顶为离当前最近的一个timer。

timer定制完毕后就可以调用ev_run函数开始event loop了。在ev_run函数中,关于timer的代码流程大致如下:

int
ev_run (EV_P_ int flags)
{
  ....

  do
    {
    ..../* 计算epoll等事件驱动机制的阻塞时间 */
      {/* 堆顶取出ANHE,减去当前时间,赋值给waittime */
            /* 调整waittime */

            backend_poll (EV_A_ waittime);        /* epoll_poll() */
      }
      ....
      /* queue pending timers and reschedule them */
      /* 调整堆,取出所有超时的timer */
      timers_reify (EV_A); /* relative timers called last */
      /* 调用pendings数组中watcher的回调函数 */
      EV_INVOKE_PENDING;
    }
}

根据最小堆timers计算事件驱动机制的阻塞等待时间,阻塞返回后timers_reify函数记录所有超时的timer,最后由EV_INVOKE_PENDING执行这些timer对应的超时回调函数。在timers_reify函数中,如果timer是单次触发类型,那么会把该timer从最小堆中删除,如果timer是重复触发类型,那么会把该timer重新放入最小堆中,等待下次触发。

参考:

http://my.oschina.net/u/917596/blog/177300

时间: 2024-08-11 07:49:58

Libev学习笔记4的相关文章

Libev学习笔记1

和Libevent相似,Libev是一个高性事件驱动框架,据说性能比Libevent要高,bug比Libevent要少.Libev只是一个事件驱动框架,不是网络库,因为它的内部并没有任何socket编程代码.支持的事件驱动机制包括: select poll epoll kqueue Solaris-specific event port mechanisms 支持的事件类型也很多,下面会全部列出. 官方首页地址:http://software.schmorp.de/pkg/libev.html

Libev学习笔记3

设置完需要监听的事件之后,就开始event loop了.在Libev中,该工作由ev_run函数完成.它的大致流程如下: int ev_run (EV_P_ int flags) { do { /* 执行EV_FORK类型事件 */ /* 执行EV_PREPARE类型事件 */ /* 遍历fdchanges数组,使用户关注的事件和epoll关注的事件保持一致 */ fd_reify (EV_A); /* 计算epoll阻塞时间 */ /* 事件驱动机制阻塞等待事件发生 */ backend_po

Libev学习笔记2

这一节根据官方文档给出的简单示例,深入代码内部,了解其实现机制.示例代码如下: int main (void) { struct ev_loop *loop = EV_DEFAULT; ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ 0, EV_READ); ev_io_start (loop, &stdin_watcher); ev_timer_init (&timeout_watcher, timeout_cb, 5

libev学习笔记

libev最大的特点是采用了轮询文件描述符(select,poll,epoll,iocp,kqueue)的方式来代替线程调度和切换,省去了线程切换,效率很高. 用小顶堆而不是链表来管理定时器,有以下优势: 1.容器中的元素实现有序排列(当然链表也能做有序排列,但性能不行,参见后面几点),这样在轮询时只需要检查前几个元素,而不需要遍历所有元素. 2.小顶堆的操作,无论是插入,还是删除,时间复杂度都在O(1)~O(logN)之间.有序链表,删除的时间复杂度为O(1),但插入的时间复杂度为O(N/2)

vector 学习笔记

vector 使用练习: /**************************************** * File Name: vector.cpp * Author: sky0917 * Created Time: 2014年04月27日 11:07:33 ****************************************/ #include <iostream> #include <vector> using namespace std; int main

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则

Caliburn.Micro学习笔记(一)----引导类和命名匹配规则 用了几天时间看了一下开源框架Caliburn.Micro 这是他源码的地址http://caliburnmicro.codeplex.com/ 文档也写的很详细,自己在看它的文档和代码时写了一些demo和笔记,还有它实现的原理记录一下 学习Caliburn.Micro要有MEF和MVVM的基础 先说一下他的命名规则和引导类 以后我会把Caliburn.Micro的 Actions IResult,IHandle ICondu

jQuery学习笔记(一):入门

jQuery学习笔记(一):入门 一.JQuery是什么 JQuery是什么?始终是萦绕在我心中的一个问题: 借鉴网上同学们的总结,可以从以下几个方面观察. 不使用JQuery时获取DOM文本的操作如下: 1 document.getElementById('info').value = 'Hello World!'; 使用JQuery时获取DOM文本操作如下: 1 $('#info').val('Hello World!'); 嗯,可以看出,使用JQuery的优势之一是可以使代码更加简练,使开

[原创]java WEB学习笔记93:Hibernate学习之路---Hibernate 缓存介绍,缓存级别,使用二级缓存的情况,二级缓存的架构集合缓存,二级缓存的并发策略,实现步骤,集合缓存,查询缓存,时间戳缓存

本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱好者,互联网技术发烧友 微博:伊直都在0221 QQ:951226918 -----------------------------------------------------------------------------------------------------------------

Activiti 学习笔记记录(三)

上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件