swoole中定时器的原理(仅epoll实现)
最近两天看到了swoole中的定时器,对其这个毫秒级的定时器产生了兴趣,因为php自带的定时器只精确到秒级,所以下载下来研究了一番
其实挺简单的,因为swool本身是采用的epoll时间循环,而epoll_wait函数的超时时间是毫秒级,所以作者正是采用的这个方式实现的
while (reactor->running > 0)
{
n = epoll_wait(epoll_fd, events, max_event_num, swReactor_get_timeout_msec(reactor));
if (n < 0)
{
continue;
}
else if (n == 0)
{
reactor->onTimeout(reactor);
SW_REACTOR_CONTINUE;
}
for (i = 0; i < n; i++)
{
//handle....
}
if (reactor->onFinish != NULL)
{
reactor->onFinish(reactor);
}
SW_REACTOR_CONTINUE;
}
上面是一段主事件循环的精简代码,而定时器的任务,则会再onTimeout、onFinish中进行处理,而epoll_wait的超时时间,则是在处理完定时器任务后计算出下一个任务的时间
例如 任务A定时1000毫秒,任务B定时2000毫秒,一切正常的情况下,会首先设置epoll_wait的超时时间1000毫秒, 然后执任务A
假设任务A的执行花了500毫秒,那么下一次定时时间就是500毫秒
对于任务B而言,首先等待了1000毫秒,然后任务A执行又会花了500毫秒,所以只需要再等待500毫秒即可
但是这样也会带来一个问题
例如,假设任务A的执行花了2000毫秒,那么对于任务B而言,时间就过去了3000毫秒,与最开始设定的2000毫秒不同,相当于任务被延迟
当然,对于这种情况而言,swoole会立马执行任务B,但是过去的时间是不可能被弥补回来
而且由于单线程的原因,所以像任务A执行花费了2000毫秒的情况,swoole的这个线程是无法响应其他网络操作的,对于这种耗时任务,swoole也是提供了Task模块,而且task任务是支持定时器的,所以推荐将许多慢速任务扔到Task中进行处理