swoole中定时器的原理(仅epoll实现)

2019年5月13日 0 作者 筱枫

最近两天看到了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中进行处理