当前位置: 首页 > 文档资料 > Swoole 中文文档 >

定时器 Timer

优质
小牛编辑
134浏览
2023-12-01

毫秒精度的定时器。底层基于epoll_waitsetitimer实现,数据结构使用最小堆,可支持添加大量定时器。

  • 在同步IO进程中使用setitimer和信号实现,如ManagerTaskWorker进程
  • 在异步IO进程中使用epoll_wait/kevent/poll/select超时时间实现

性能

底层使用最小堆数据结构实现定时器,定时器的添加和删除,全部为内存操作,因此性能是非常高的。

官方的基准测试脚本 https://github.com/swoole/swoole-src/blob/master/benchmark/timer.php 中,添加或删除10万个随机时间的定时器耗时为0.08s左右。

~/workspace/swoole/benchmark$ php timer.php
add 100000 timer :0.091133117675781s
del 100000 timer :0.084658145904541s

!> 定时器是内存操作,无IO消耗

差异

TimerPHP本身的pcntl_alarm是不同的。pcntl_alarm是基于时钟信号 + tick函数实现存在一些缺陷:

  • 最大仅支持到秒,而Timer可以到毫秒级别
  • 不支持同时设定多个定时器程序
  • pcntl_alarm依赖declare(ticks = 1),性能很差

零毫秒定时器

底层不支持时间参数为0的定时器。这与Node.js等编程语言不同。在Swoole里可以使用Swoole\Event::defer实现类似的功能。

Swoole\Event::defer(function () {
  echo "hello\n";
});

!> 上述代码与JS中的setTimeout(func, 0)效果是完全一致的。

别名

tick()after()clear()都拥有一个函数风格的别名

类静态方法函数风格别名
Swoole\Timer::tick()swoole_timer_tick()
Swoole\Timer::after()swoole_timer_after()
Swoole\Timer::clear()swoole_timer_clear()

方法

tick()

设置一个间隔时钟定时器。

after定时器不同的是tick定时器会持续触发,直到调用 Timer::clear 清除。

Swoole\Timer::tick(int $msec, callable $callback_function, ...$params): int;

!> 1. 定时器仅在当前进程空间内有效
2. 定时器是纯异步实现的,不能与同步IO的函数一起使用,否则定时器的执行时间会发生错乱
3. 定时器在执行的过程中可能存在一定误差

  • 参数

    • int $msec

      • 功能:指定时间
      • 值单位:毫秒【如1000表示1秒,v4.2.10以下版本最大不得超过 86400000
      • 默认值:无
      • 其它值:无
    • callable $callback_function

      • 功能:时间到期后所执行的函数,必须是可以调用的
      • 默认值:无
      • 其它值:无
    • ...$params

      • 功能:给执行函数传递数据【此参数也为可选参数】
      • 默认值:无
      • 其它值:无

      !> 可以使用匿名函数的use语法传递参数到回调函数中

  • $callback_function 回调函数

    callbackFunction(int $timer_id, ...$params);
    • int $timer_id

      • 功能:定时器的ID【可用于Timer::clear清除此定时器】
      • 默认值:无
      • 其它值:无
    • ...$params

      • 功能:由Timer::tick传入的第三个参数$param
      • 默认值:无
      • 其它值:无
  • 扩展

    • 定时器校正

      定时器回调函数的执行时间不影响下一次定时器执行的时间。实例:在0.002s设置了10mstick定时器,第一次会在0.012s执行回调函数,如果回调函数执行了5ms,下一次定时器仍然会在0.022s时触发,而不是0.027s

      但如果定时器回调函数的执行时间过长,甚至覆盖了下一次定时器执行的时间。底层会进行时间校正,丢弃已过期的行为,在下一时间回调。如上面例子中0.012s时的回调函数执行了15ms,本该在0.022s产生一次定时回调。实际上本次定时器在0.027s才返回,这时定时早已过期。底层会在0.032s时再次触发定时器回调。

    • 协程模式

      在协程环境下Timer::tick回调中会自动创建一个协程,可以直接使用协程相关API,无需调用go创建协程。

      !> 可设置 enable_coroutine 关闭自动创建协程

  • 使用示例

    Swoole\Timer::tick(1000, function(){
        echo "timeout\n";
    });
    • 正确示例
    Swoole\Timer::tick(3000, function (int $timer_id, $param1, $param2) {
        echo "timer_id #$timer_id, after 3000ms.\n";
        echo "param1 is $param1, param2 is $param2.\n";
    
        Swoole\Timer::tick(14000, function ($timer_id) {
            echo "timer_id #$timer_id, after 14000ms.\n";
        });
    }, "A", "B");
    • 错误示例
    Swoole\Timer::tick(3000, function () {
        echo "after 3000ms.\n";
        sleep(14);
        echo "after 14000ms.\n";
    });

after()

在指定的时间后执行函数。Swoole\Timer::after函数是一个一次性定时器,执行完成后就会销毁。

此函数与PHP标准库提供的sleep函数不同,after是非阻塞的。而sleep调用后会导致当前的进程进入阻塞,将无法处理新的请求。

Swoole\Timer::after(int $msec, callable $callback_function, ...$params): int;
  • 参数

    • int $msec

      • 功能:指定时间
      • 值单位:毫秒【如1000表示1秒,v4.2.10以下版本最大不得超过 86400000
      • 默认值:无
      • 其它值:无
    • callable $callback_function

      • 功能:时间到期后所执行的函数,必须是可以调用的。
      • 默认值:无
      • 其它值:无
    • ...$params

      • 功能:给执行函数传递数据【此参数也为可选参数】
      • 默认值:无
      • 其它值:无

      !> 可以使用匿名函数的use语法传递参数到回调函数中

  • 返回值

  • 扩展

    • 协程模式

      在协程环境下Swoole\Timer::after回调中会自动创建一个协程,可以直接使用协程相关API,无需调用go创建协程。

      !> 可设置 enable_coroutine 关闭自动创建协程

  • 使用示例

    $str = "Swoole";
    Swoole\Timer::after(1000, function() use ($str) {
        echo "Hello, $str\n";
    });

clear()

使用定时器ID来删除定时器。

Swoole\Timer::clear(int $timer_id): bool;
  • 参数

    • int $timer_id
      • 功能:定时器ID【调用Timer::tickTimer::after后会返回一个整数的ID】
      • 默认值:无
      • 其它值:无

!> Swoole\Timer::clear不能用于清除其他进程的定时器,只作用于当前进程

  • 使用示例

    $timer = Swoole\Timer::after(1000, function () {
        echo "timeout\n";
    });
    
    var_dump(Swoole\Timer::clear($timer));
    var_dump($timer);
    
    // 输出:bool(true) int(1)
    // 不输出:timeout

clearAll()

清除当前工作进程内的所有定时器。

!> 需要Swoole版本 >= v4.4.0

Swoole\Timer::clearAll(): bool;

info()

返回timer的信息。

!> 需要Swoole版本 >= v4.4.0

Swoole\Timer::info(int $id): array;
  • 返回值

    array(4) {
      ["exec_msec"]=>
      int(162)
      ["interval"]=>
      int(0)
      ["round"]=>
      int(0)
      ["removed"]=>
      bool(false)
    }

list()

返回定时器迭代器, 可使用foreach遍历全局所有timer的 id

!> 需要Swoole版本 >= v4.4.0

Swoole\Timer::list(): Swoole\Timer\Iterator;
  • 返回值

    返回定时器迭代器, 可使用foreach遍历全局所有timerid

  • 使用示例

    foreach (Swoole\Timer::list() as $timer_id) {
        var_dump(Swoole\Timer::info($timer_id));
    }

stats()

查看定时器状态。

!> 需要Swoole版本 >= v4.4.0

Swoole\Timer::stats(): array;
  • 返回值

    array(3) {
      ["initialized"]=>
      bool(true)
      ["num"]=>
      int(1000)
      ["round"]=>
      int(1)
    }

set()

设置定时器相关参数。

Swoole\Timer::set(array $array): void;

关闭协程 :id=close-timer-co

默认定时器在执行回调函数时会自动创建协程,可单独设置定时器关闭协程。

Swoole\Timer::set([
  'enable_coroutine' => false,
]);