Libev——ev_timer 相对时间定时器
Libev中的超时监视器ev_timer,是简单的相对时间定时器,它会在给定的时间点触发超时事件,还可以在固定的时间间隔之后再次触发超时事件。
1.超时监视器ev_timer结构
1 2 3 4 5 6 7 8 9 10 11 | typedef struct ev_timer { /*前五行为EV_WATCHER 参数具体含义在<a id="cb_post_title_url" class="postTitle2 vertical-middle" href="https://www.cnblogs.com/c12345/p/15621468.html">libev I/O事件</a>中有描述*/ 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
1 2 3 | #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(这里使用小顶堆,即子节点均大于父节点,即堆顶元素所等待激活事件是最小的)
1 | //----- 宏EV_HEAP_CACHE_AT是为了提高在堆中的缓存利用率,主要是为了对at进行缓at |
宏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_start
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成员,表明在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);
// 调整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)]);//从watcher中获取at值 //因为新添加的元素放在数组后面,然后向上调整堆 upheap (timers, ev_active (w)); EV_FREQUENT_CHECK; /*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/ }
当对某一节点执行 upheap()
时,就是与其父节点进行比较,如果其值比父节点小,则交换,然后在对这个父节点重复 upheap()
,直到顶层或其值大于父节点的值。
1 2 3 4 5 6 7 | 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中哪些定时器触发了,获得所有超时的timer,代码如下:
/* 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);//将rfeeds[]结构中的watcher插入到pengdings数组的操作
}
}
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停止该监视器。
用图来示意一下
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· .NET Core 托管堆内存泄露/CPU异常的常见思路
· PostgreSQL 和 SQL Server 在统计信息维护中的关键差异
· C++代码改造为UTF-8编码问题的总结
· DeepSeek 解答了困扰我五年的技术问题
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库