我对libev的ev_timer有疑问,请看下面的代码:
static void timeout_cb(struct ev_loop *loop, ev_timer *timer, int revent) {
printf("got an timeout event, current time %s\n", get_current_time());
}
int main(int argc, char *argv[]) {
struct ev_loop *loop = ev_loop_new(0);
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
ev_timer_start(loop, &timer);
ev_run(loop, EVRUN_NOWAIT); // callback should not be called
ev_timer_stop(loop, &timer); // stop the timer
sleep(5); // sleep 5 seconds, 5 is also the timer's timeout value
// restart timer
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
ev_timer_start(loop, &timer);
printf("timer start at: %s\n", get_current_time());
printf("timer remaining: %f\n", ev_timer_remaining(loop, &timer));
ev_run(loop, EVRUN_NOWAIT);
return 0;
}
输出结果为:
timer start at: 14:53:49:137
timer remaining: 5.0000
got an timeout event, current time 14:53:49:137
这很奇怪,因为在重新启动计时器后,计时器立即触发,但应该在5秒之后。我发现原因是睡眠(5)。我将其更改为sleep(4),然后不会调用计时器回调。我对liebev的计时器功能感到困惑。我对计时器有误解吗?如何在定时器重启后超时后调用定时器回调?
答案 0 :(得分:2)
您应该更改代码
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
到
ev_timer_init(&timer, timeout_cb, ev_time() - ev_now() + 5.0, 0.0);
因为libev会因性能原因缓存时间戳,并且只在每个事件循环之前或之后更新时间戳。在您休眠五秒后,当前实时将为T + 5
,即ev_time
,但libev的当前时间戳仍为T
,即ev_now
。那么你就像这样启动一个计时器
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
libev会将当前时间戳加上5的定时器触发时间设置为T + 5
,也是当前实时,因此会立即触发。但是当你像这样启动计时器时
ev_timer_init(&timer, timeout_cb, ev_time() - ev_now() + 5.0, 0.0);
libev将在当前时间戳脉冲ev_time() - ev_now() + 5.0
设置定时器的触发时间,即T + T + 5 - T + 5 = T + 5 + 5 = real time + 5
。所以它会在五秒钟后触发。
答案 1 :(得分:1)
我并不是自称是libev的专家,但我发现使用ev_suspend(循环)和ev_resume(循环)会阻止你看到的行为。从手册页:
ev_suspend (loop) ev_resume (loop) These two functions suspend and resume an event loop, for use when the loop is not used for a while and timeouts should not be processed.
E.g:
ev_run(loop, EVRUN_NOWAIT); // callback should not be called
ev_timer_stop(loop, &timer); // stop the timer
ev_suspend(loop); // suspend timer processing
sleep(5); // sleep 5 seconds, 5 is also the timer's timeout value
ev_resume(loop); // resume timer processing
// restart timer
ev_timer_init(&timer, timeout_cb, 5.0, 0.0);
ev_timer_start(loop, &timer);
此外,我发现另一种可能的解决方案是使用ev_timer_again()而不是ev_timer_start(),这也会阻止您报告的行为。
E.g:
ev_timer_stop(loop, &timer); // stop the timer
sleep(5); // sleep 5 seconds, 5 is also the timer's timeout value
// restart timer
ev_timer_again(loop, &timer);