和 JS 一样的 php setTimeout 函数

2016-07-08 16:36:54 +08:00
 szopen
技术依赖

需要 PHP 5.5 以上,支持生成器
需要支持 ticks


实现代码见
http://toknot.com/blog/php-setTimeout.html

主要利用生成器实现,和 js 的一样,不会阻塞脚本执行(不同于 sleep 的阻塞)
回调函数一定会在延迟时间执行。延迟时间单位是毫秒,千分之一秒
4021 次点击
所在节点    PHP
11 条回复
laoyuan
2016-07-08 17:39:47 +08:00
PHP7 跑通了,确实非阻塞。
5.5.34 这一行
function setTimeout(callable $callback, int $time) : Generator{
报错
PHP Parse error: parse error, expecting `'{''
qa2080639
2016-07-08 19:07:13 +08:00
@laoyuan 5.5.12 +1
shyling
2016-07-08 19:22:42 +08:00
咦,我不认识的 php 。马克
Balthild
2016-07-09 01:49:58 +08:00
Tick 函数啊,有意思。
这个东西文档很少,也几乎没见到过人用,据说性能不好…
dawniii
2016-07-09 07:54:38 +08:00
declare 据说不应该是过时的吗?每走一行代码就去检测任务。效率很低。
这段代码的原理就是每走一行代码就去检测是否有定时任务。。。
最后再加上 register_shutdown_function 这个函数。等脚本结束了,执行这个函数继续检测任务。
可以去看看 workerman 的定时器实现。
jhdxr
2016-07-09 14:38:20 +08:00
@dawniii declare 没过时,过时的只是它的一种用法,也就是 @Balthild 说的 tick ,效率的确很低。
workerman 的 timer 实现的方式也类似。用的是 pcntl 库的函数,每秒给自己发一个 alarm ,去检查下有没有要执行的东西,效率也不高_(:з」∠)_
其实 php 自己没有 ** 原生很好 ** 的支持多线程,想要高效的去实现这种异步的东西不大可能_(:з」∠)_ 还是上扩展吧,比如 swoole 。。。
jhdxr
2016-07-09 14:38:55 +08:00
@laoyuan 因为 LZ 用了一个 7 的语法,返回值类型声明,` : Generator`去掉即可
dawniii
2016-07-09 19:03:00 +08:00
@jhdxr 感谢 明白了。但是我看 workerman 的定时器好像是有两个流程 一个是在没有注册 event 的时候走信号,另一个是走 event (我记得 workerman 启动过程会注册 event, select 或者 libevent )。如果是这样效率应该是可以的。还有就是 LZ 代码我试着改了改,不用 yield 就能实现。。。
szopen
2016-07-11 14:40:02 +08:00
@dawniii 的确可以不用 yield 。另外 tick 效率的确十分低下,据分析,当启用 tick 后, PHP 会在 所有 opcode 后插入 TICK 的 opcode,
PHP 7.1 的 Asynchronous Signal Handling 或许会解决 pcntl 信号的效率问题 https://wiki.php.net/rfc/async_signals
szopen
2016-07-11 14:55:36 +08:00
@dawniii 如果不用 yield ,可能会加重 tick hander 函数的复杂度, setTimeout 函数实现也会变得复杂起来
dawniii
2016-07-11 21:34:11 +08:00
@szopen 貌似没有复杂啊

<?php
//enable ticks
declare (ticks = 1);

//setTimeout event list
$timeoutQueue = new SplObjectStorage;

register_tick_function(function() {
global $timeoutQueue;
foreach ($timeoutQueue as $v) {
$callBack = $v();
if (is_callable($callBack)) {
$callBack();
$timeoutQueue->detach($v);
}
}
});

register_shutdown_function(function() {
global $timeoutQueue;
while($timeoutQueue->count()) {
foreach ($timeoutQueue as $v) {
$callBack = $v();
if (is_callable($callBack)) {
$callBack();
$timeoutQueue->detach($v);
}
}
usleep(1000);
}
});

function setTimeOut($callback, $time)
{
global $timeoutQueue;
$end = microtime(true) * 1000 + $time;

$f = function() use ($callback,$end) {
if (microtime(true)*1000 >= $end) {
return $callback;
} else {
return false;
}
};
$timeoutQueue->attach($f);
return $f;
}

function clearTimeout(Closure $f) {
global $timeoutQueue;
$timeoutQueue->detach($f);
}

setTimeOut(function() {
echo 'aaaa';
},3000);
setTimeOut(function() {
echo 'bbbb';
},6000);

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/291187

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX