核心:秒级定时器 - lizhibin205/php_crond GitHub Wiki

秒级定时器

秒级定时器是通过包react/event-loop实现的

$loop = \React\EventLoop\Factory::create();
$loop->addPeriodicTimer(1, function($timer) use ($loop) {
    //your code
});
$loop->run();

event-loop实现方式

namespace React\EventLoop;

/**
 * The `Factory` class exists as a convenient way to pick the best available event loop implementation.
 */
final class Factory
{
    /**
     * Creates a new event loop instance
     *
     * ```php
     * $loop = React\EventLoop\Factory::create();
     * ```
     *
     * This method always returns an instance implementing `LoopInterface`,
     * the actual event loop implementation is an implementation detail.
     *
     * This method should usually only be called once at the beginning of the program.
     *
     * @return LoopInterface
     */
    public static function create()
    {
        // @codeCoverageIgnoreStart
        if (\function_exists('uv_loop_new')) {
            // only use ext-uv on PHP 7
            return new ExtUvLoop();
        } elseif (\class_exists('libev\EventLoop', false)) {
            return new ExtLibevLoop();
        } elseif (\class_exists('EvLoop', false)) {
            return new ExtEvLoop();
        } elseif (\class_exists('EventBase', false)) {
            return new ExtEventLoop();
        } elseif (\function_exists('event_base_new') && \PHP_MAJOR_VERSION === 5) {
            // only use ext-libevent on PHP 5 for now
            return new ExtLibeventLoop();
        }

        return new StreamSelectLoop();
        // @codeCoverageIgnoreEnd
    }
}

StreamSelectLoop如何工作

1. 添加周期事件

$loop->addPeriodicTimer(1, function($timer) use ($loop) {
    //wort code
});

addPeriodicTimer方法将会生成一个React\EventLoop\Timer\Timer对象,并使用React\EventLoop\Timer\Timers进行管理。 Timer将会存储于SplObjectStorage中,$storage[$timer] = [下次执行时间],并且插入优先级队列SplPriorityQueue(该队列优先级为- ([事件间隔] + [当前时间]))。因此,队列第一个元素就是下个次执行的事件Timer。

2. 执行run方法

在run方法中,React\EventLoop\Timer\Timers对象会执行tick()方法,该方法会从SplPriorityQueue中不断取出Timer,并执行Timer的callback,直到队列为空,如果Timer是周期性事件,则重新放到SplPriorityQueue队列。

3. usleep

计算方式:SplPriorityQueue队列第一个元素未来执行时间 - 当前时间。比如档期时间是1539582930,3个Timer分别是3,4,5,则sleep时间就是3秒。

警告:由于这种方式是单进程处理,不要在Timer的callback执行阻塞操作。否则会导致某个周期的Timer无法执行。