Libev——ev_timer 相对时间定时器
Libev中的超时监视器ev_timer,是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件。
1.超时监视器ev_timer结构
typedef struct ev_timer
{
/*前五行为EV_WATCHER 参数具体含义在libev I/O事件中有描述*/
int active;
int pending;
int priority;
void *data;
void (*cb)(struct ev_loop *loop, struct ev_timer *w, int revents); //回调函数 ev_tstamp at; // 定时器第一次触发的时间点,根据mn_now设置
ev_tstamp repeat; //repeat 必须>=0,当大于0时表示每隔repeat秒该定时器再次触发;0表示只触发一次;double 型
} ev_timer;watcher_
at和repeat两个成员为是ev_timer特有的
2.ev_watcher_timer
#define EV_WATCHER_TIME(type) \ //定时器
EV_WATCHER (type) \
ev_tstamp at; // 定时器第一次触发的时间点,根据mn_now设置
ev_watcher_time的结构与ev_timer几乎一样,只是少了最后一个成员。该结构其实是ev_timer和ev_periodic的父类,它包含了ev_timer和ev_periodic的共有成员。
3.堆元素ANHE
//----- 宏EV_HEAP_CACHE_AT是为了提高在堆中的缓存利用率,主要是为了对at进行缓
#if EV_HEAP_CACHE_AT
typedef struct
{
ev_tstamp at;
WT w;
} ANHE;//除了包含该指针WT之外,还缓存了ev_watcher_time
中的成员at
,堆中元素就是根据at
的值进行组织的,具有最小at
值得节点就是根节点。
#else
typedef WT ANHE;//是一个指向时间监视器结构ev_watcher_time
的指针WT
#endif
宏EV_HEAP_CACHE_AT的作用,是为了提高在堆中的缓存利用率,如果没有定义该宏,堆元素就是指向ev_watcher_time结构的指针。如果定义了该宏,则还将堆元素的关键成员at进行缓存。
4.定时器事件的初始化与设置
//初始化,意味着在after秒后执行,设置为0则会立即执行一次;然后每隔repeat秒执行一次
#define ev_timer_init(ev,cb,after,repeat) \
do { ev_init ((ev), (cb)); ev_timer_set ((ev),(after),(repeat)); } while (0)
//设置
#define ev_timer_set(ev,after_,repeat_)
do { ((ev_watcher_time *)(ev))->at = (after_); (ev)->repeat = (repeat_); } while (0)
5.启动定时器ev_timer_stavoid noinlinev_timer_start (EV_P_ ev_timer *w) EV_THROW
{
if (expect_false (ev_is_active (w)))
return; ev_at (w) += mn_now;//首先设置监视器的at成员,表明在at时间点,超时事件会触发,注意at是根据mn_now设置的,也就是相对于系统启动时间而言的(或者是日历时间) assert (("libev: ev_timer_start called with negative timer repeat value", w->repeat >= 0.)); EV_FREQUENT_CHECK; //定时器数量 有多少个定时器
++timercnt;
//事件激活相关 其中active即为timercnt(timers下标)(这一块还要仔细看一下)
ev_start (EV_A_ (W)w, timercnt + HEAP0 - 1);
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)]); //新添加的元素放在数组后面,然后向上调整堆 (怎么调整的)
upheap (timers, ev_active (w)); EV_FREQUENT_CHECK; /*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
}
当对某一节点执行 upheap()
时,就是与其父节点进行比较,如果其值比父节点小,则交换,然后在对这个父节点重复 upheap()
,直到顶层。
inline_speed void
ev_start (EV_P_ W w, int active)
{
pri_adjust (EV_A_ w);
w->active = active;//监视器的active成员,表明该监视器在堆数组中的下标
ev_ref (EV_A);
}
6 timers_reify
每次调用backend_poll之前,都会根据ANHE_at (timers [HEAP0]) - mn_now的值,校准backend_poll的阻塞时间waittime,这样就能尽可能的保证定时器能够按时触发。
调用backend_poll之后,就会调用timers_reify查看timers中哪些定时器触发了,代码如下:
/* make timers pending 挂起状态*/
inline_size void
timers_reify (EV_P)
{
EV_FREQUENT_CHECK; if (timercnt && ANHE_at (timers [HEAP0]) < mn_now)//定时器值不为0,且堆顶值小于,mn_now相对于系统启动时间
{
//timercnt 为定时器的值,在timer_start中累加
do
{
ev_timer *w = (ev_timer *)ANHE_w (timers [HEAP0]); /*assert (("libev: inactive timer on timer heap detected", ev_is_active (w)));*/ /* first reschedule or stop timer */ if (w->repeat)//若为重复定时器,一次性定时器w->repeat=0
{
ev_at (w) += w->repeat;//启动时间加重复时间
if (ev_at (w) < mn_now)//还小于当前时间
ev_at (w) = mn_now;//赋值当前时间 assert (("libev: negative ev_timer repeat value found while processing timers", w->repeat > 0.)); ANHE_at_cache (timers [HEAP0]);// #define ANHE_at_cache(he) (he).at = (he).w->at /* update at from watcher */
//向下调整堆,定时器仍然存在该数组中
downheap (timers, timercnt, HEAP0);
}
else
ev_timer_stop (EV_A_ w); /* nonrepeating: stop timer 一次性定时器 停止 */ EV_FREQUENT_CHECK;
//将超时的事件加入rfeeds[]结构中
feed_reverse (EV_A_ (W)w);
}
while (timercnt && ANHE_at (timers [HEAP0]) < mn_now);//定时器不为0且堆顶的值一直小于当前时间就重复上述操作 feed_reverse_done (EV_A_ EV_TIMER);
}
}
定时器就是在backend_poll之前通过定时器堆顶的超时时间,保证blocking的时间不超过最近的定时器时间,在backend_poll返回后,从定时器堆中取得超时的watcher放入到pendings二维数组中,从而在后续处理中可以执行其上注册的触发动作。然后从定时器管理堆上删除该定时器。最后调用和ev_start呼应的ev_stop修改驱动器loop的状态,即loop->activecnt减少一。并将该watcher的active置零。
6 停止定时器ev_stop_timer
void noinline
ev_timer_stop (EV_P_ ev_timer *w) EV_THROW
{ //清除pendings[]激活事件队列中,关于w的事件
clear_pending (EV_A_ (W)w);
if (expect_false (!ev_is_active (w)))
return; EV_FREQUENT_CHECK; {
int active = ev_active (w);//这个地方还不是很理解 assert (("libev: internal timer heap corruption", ANHE_w (timers [active]) == (WT)w));
//定时器数量减1
--timercnt; if (expect_true (active < timercnt + HEAP0))
{ //timers [active]中的元素用数组最后一个元素替换,最后一个元素正常情况下为最大值
timers [active] = timers [timercnt + HEAP0]; //比较下标为active处的元素与其父节点的元素,来决定采用向上、向下调整
adjustheap (timers, timercnt, active);
}
} ev_at (w) -= mn_now; ev_stop (EV_A_ (W)w); EV_FREQUENT_CHECK;
}
首先调用clear_pending,如果该监视器已经处于pending状态,将其从pendings中删除。然后根据监视器中的active成员,得到其在timers堆上的索引,将该监视器从堆timers上删除,重新调整堆结构。然后调用ev_stop停止该监视器。
用图来示意一下
Libev——ev_timer 相对时间定时器的更多相关文章
- Libev源码分析04:Libev中的相对时间定时器
Libev中的超时监视器ev_timer,就是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件. 所谓的相对时间,指的是如果你注册了一个1小时的超时事件 ...
- libev中timer时间事件监控器
1.数据结构 #define ev_at(w) ((WT)(w))->at#define ev_active(w) ((W)(w))->active typedef ev_watcher_ ...
- Libev源码分析03:Libev使用堆管理定时器
Libev中在管理定时器时,使用了堆这种结构,而且除了常见的最小2叉堆之外,它还实现了更高效的4叉堆. 之所以要实现4叉堆,是因为普通2叉堆的缓存效率较低,所谓缓存效率低,也就是说对CPU缓存的利用率 ...
- [置顶] ios 时间定时器 NSTimer应用demo
原创文章,转载请注明出处:http://blog.csdn.net/donny_zhang/article/details/9251917 demo功能:ios NSTimer应用demo .ipho ...
- spring实现可重置时间定时器
此文章是基于 搭建Jquery+SpringMVC+Spring+Hibernate+MySQL平台 一. jar包介绍 1. spring-framework-4.3.4.RELEASE 的 lib ...
- 2018.7.6 js实现点击事件---点击小图出现大图---时间定时器----注册表单验证
<!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...
- Libev源码分析05:Libev中的绝对时间定时器
Libev中的超时监视器ev_periodic,是绝对时间定时器,不同于ev_timer,它是基于日历时间的.比如如果指定一个ev_periodic在10秒之后触发(ev_now() + 10),然后 ...
- spring实现固定时间定时器
此文章是基于 搭建Jquery+SpringMVC+Spring+Hibernate+MySQL平台 一. jar包介绍 1. spring-framework-4.3.4.RELEASE 的 lib ...
- 《Linux内核设计与实现》读书笔记(十一)- 定时器和时间管理【转】
转自:http://www.cnblogs.com/wang_yb/archive/2013/05/10/3070373.html 系统中有很多与时间相关的程序(比如定期执行的任务,某一时间执行的任务 ...
随机推荐
- GoLang设计模式15 - 策略模式
策略模式是一种行为型设计模式.通过策略模式,可以在运行时修改一个对象的行为. 接下来仍然是通过例子来了解策略模式.比如说内存缓存,这是我们在开发中经常使用的东西,大家应该都有一定的了解,接下来就用内存 ...
- MyBatis 中为什么不建议使用 where 1=1?
最近接手了一个老项目,"愉悦的心情"自然无以言表,做开发的朋友都懂,这里就不多说了,都是泪... 接手老项目,自然是要先熟悉一下业务代码,然而在翻阅 mapper 文件时,发现 ...
- 3组-Alpha冲刺-1/6
一.基本情况 队名:发际线和我作队 组长博客:链接 小组人数:10 二.冲刺概况汇报 黄新成(组长) 过去两天完成了哪些任务 文字描述 组织会议,讨论了alpha冲刺的分工,确定了收集数据的渠道,为拍 ...
- Maven 依赖调解源码解析(六):dependencyManagement 版本锁定
本文是系列文章<Maven 源码解析:依赖调解是如何实现的?>第六篇,主要介绍 dependencyManagement 版本锁定原则.请按顺序阅读其他系列文章,系列文章总目录参见:htt ...
- [luogu5654]基础函数练习题
答案即区间$[l,r]$的笛卡尔树上,左右子树有一个为空的点到根路径和(定义此为的该点答案)的max, 对求区间笛卡尔树复杂度为$o(n)$,无法通过,因此在全局笛卡尔树中考虑此问题 设$k$为$l$ ...
- [loj3175]排列鞋子
贪心与最近的鞋子匹配(大小相同且方向相反),记$a_{x}$表示第x双鞋子的左位置,$b_{x}$表示右位置 若$a_{x}>b_{x}$,那么可以交换这两双鞋子并令答案+1,所以不妨设$a_{ ...
- Cortex-A系列中断
1. 回顾STM32系统 1.1 中断向量表 ARM芯片冲0x00000000,在程序开始的地方存放中断向量表,按下中断时,就相当于告诉CPU进入的函数.描述很多个中断服务函数的表. 对于STM32来 ...
- AgileConfig-1.5.5 发布 - 支持 JSON 编辑模式
本次更新加入了2个新的编辑模式:JSON 编辑模式.TEXT 编辑模式.特别是 JSON 编辑模式是大家比较期待的一个功能.因为大家都习惯了 appsettings.json 的配置编辑模式,所以天生 ...
- C#/VB.NET 将彩色PDF转为灰度PDF
本文以C#代码为例介绍如何实现将彩色PDF文件转为灰度(黑白)的PDF文件,即 将PDF文档里面的彩色图片或者文字等通过调用PdfGrayConverter.ToGrayPdf()方法转为文档页面为灰 ...
- mybatis避免sql的like注入
<select id="NotInByEvalQuestion" resultType="com.rm.eval.entity.EvalQnQuestion&quo ...