php使用singal为什么需要使用declare(ticks=n)语句?
pcntl 拓展在实现signal上使用了“延后执行”的机制;因此使用该功能时,必须先使用语句declare(ticks=1),否则注册的singal-handel就不会执行了
检测信号不一定非要用 declare(ticks=n)。 在合适的时机调用 pcntl_signal_dispatch() 也可以,这样没有性能损耗
刚看到这个,说性能很低 http://rango.swoole.com/archives/364
declare(ticks=n) 性能低,workerman没有使用它
感谢群主
发表一下我的观点
方式一://优点:及时 能打断系统中断 比如sleep 缺点性能相对低 declare(ticks = 1); //此语句用于说明程序每tick一次检查一遍有没有信号触发 pcntl_signal(SIGINT, 'signalHandler'); // 设置对应信号的回调函数
方式二: //优点性能相对高 缺点:在中间部分有sleep等类似函数无法及时执行回调函数 pcntl_signal(SIGINT, 'signalHandler'); // 设置对应信号的回调函数 //XXXX 此处是自己的业务逻辑 pcntl_signal_dispatch();函数用于在当前位置判断期间有没有触发信号,然后调用对应的回调函数
总结: pcntl_signal函数设置信号回调函数后必须要有触发点,declare或者pcntl_signal_dispatch二选一
感谢解答
学习了!
是不是由于群主使用方式二:pcntl_signal_dispatch(),所以应该在业务代码中间部分应该避免使用sleep等类似函数导致无法及时执行回调函数?
pcntl_signal_dispatch()
sleep
刚开始是被 http://rango.swoole.com/archives/364 这篇文档的 这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。因为PHP的函数无法直接注册到操作系统信号设置中 这段代码误导了,一直只有declare(ticks = 1)才会注册到操作系统信号设置中
这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。因为PHP的函数无法直接注册到操作系统信号设置中
declare(ticks = 1)
看了下,感觉文章不严谨,并非pcntl_signal性能差,而是declare(ticks = 1)性能差。
2018年的时候遇到过类似问题并对此有过一个总结,相关点分享过来:
4、信号回调是不会自己自动执行的,要么主动声明declare(ticks=1),要么主动调用pcntl_signal_dispatch检查信号以执行信号回调处理函数,推荐高性能pcntl_singal_dispatch。 5、pcntl_signal_dispatch函数的作用:检测信号队列里是否有信号发生,如果有,则执行进程绑定的信号处理回调函数。
完整总结可以去看我原贴: http://www.blogdaren.com/post-2375.html
感谢大佬分享!但是Timer类,并没有找到代理触发了pcntl_signal_dispatch函数
pcntl_signal_dispatch
workerman的主进程和子进程对于定时器实现用的并不是同一套机制, 主进程用的是pcntl_signal相关技术,子进程则用的是event内置的相关技术,在workerman源码中我们不难发现主进程的大LOOP里明显能看到pcntl_signal_dispatch的影子。
子进程应该是通过 https://www.php.net/manual/zh/event.add.php 这个实现的
只能说默认用的是event库,也可以手动配置为调用其他网络事件库比如libevent或者swoole啥的;另不管是哪个网络事件库,对定时器实现而言都是透明的。
现在是有点想知道是怎么实现的了,哈哈
主进程使用的是pcntl_alarm做定时(秒级)
子进程event/libevent/stream_select超时 (毫秒级)
select作为事件轮训器会在每次tick的时候调用pcntl_signal_dispatch 查询有无信号需要处理
event事件轮训器不会每次调用pcntl_signal_dispatch,也就是在业务代码中 写的pcntl_signal(SIGALRM, 'signalHandler')无效
Timer类,没有找到代理触发了pcntl_signal_dispatch函数 因为在具体的事件轮训器类里面
最后这两句话我认为理解不正确,就workerman的主进程而言,其用的是alarm机制,换句话:pcntl_alarm是在给定的时间之后给当前进程发送时钟信号,同样也是需要主动调用dispatch去检测下信号的,workerman的主进程代码空间的大LOOP【见Worker::monitorWorkersForLinux()】里的dispatch就是用来干这个事情的,和具体的事件轮询类没有任何关系; 而子进程的定时器实现才会依赖到各个网络事件库。
感觉主进程是完全脱离第三方事件库的。pcntl_alarm(1) 也就是每秒给主进程发送时钟信号
pcntl_alarm(1)
循环是在等子进程的返回。同时也会检测信号,和事件轮询是有关系的,你看每次tick都在检查有无事件需要处理,可以去群里聊官方五群
是的,检测信号不只是针对主进程发送的的alarm信号,而且wait系统调用是针对子进程管理而言的,也和定时器没有什么关系; 但是主进程的定时器实现确实是没用到网络事件库的,我将一切涉及网络事件库的定时器代码已经删除并抽剥了个定时器DEMO,具体仿真代码你可以参考运行下看: http://www.blogdaren.com/post-2643.html
不知我们是不是说的一个点
还没读到这里,子进程的这个调用等待信号的处理器怎么搞的
@张先生 话题有些许出入,其次你贴的这个看上去应该是官方自带的那个基于stream_select的定时器轮询机制,这点我认同你上面说的描述,但是子进程空间的其他网络事件库针对定时器的处理基本是没有在workerman层面直接来实现的; @Tinywan 细节三两句不好说,简单说就是轮询,原理参考这么几个关键点理解下:
大佬们参考下下面序号是否合理?
<?php class Tinyman { public static $_tasks = []; public static function init() { // ① 安装时钟信号,同时设置信号处理器 signalHandler \pcntl_signal(\SIGALRM, array(self::class, 'signalHandler'), false); } public function signalHandler() { // ④ pcntl_signal_dispatch 捕捉信号,触发信号处理器。 // ⑤ 该闹钟信号同样会被 pcntl_signal_dispatch() 信号捕捉到,然后重复触发该信号处理器。通过 kill -SIGALRM 396 发送信号量也是可以的 \pcntl_alarm(1); // ⑥ 通过 tick() 钩子进行业务处理 self::tick(); } public static function tick() { // ⑦ 防止空任务时因继续生产无效时钟信号,0:表示将不会创建alarm闹钟信号 if (empty(self::$_tasks)) { \pcntl_alarm(0); return; } // ⑧ 处理业务,通过设置的 signalHandler 重复触发该信号处理器:② 捕捉信号 -> ④ 信号捕捉触发信号处理器 -> ⑥ 通过 tick() 钩子进行业务处理 echo ' [x] 主进程 tick 定时任务 '.json_encode(self::$_tasks), "\n"; } } Tinyman::init(); // ② 向当前进程发送 SIGALRM 信号 \pcntl_alarm(1); $i = 0; // 主进程初始化,不让当前进程退出 while (true) { if ($i % 5 ==0 ) { Tinyman::$_tasks[$i] = $i; echo " [x] 主进程... ".posix_getpid()." \n"; }; $i++; sleep(1); // ③ 捕捉信号 pcntl_signal_dispatch(); }
如果你是模拟官方定时器逻辑的话,这是有些问题的:
检测信号不一定非要用 declare(ticks=n)。
在合适的时机调用 pcntl_signal_dispatch() 也可以,这样没有性能损耗
刚看到这个,说性能很低 http://rango.swoole.com/archives/364
declare(ticks=n) 性能低,workerman没有使用它
感谢群主
发表一下我的观点
方式一://优点:及时 能打断系统中断 比如sleep 缺点性能相对低
declare(ticks = 1); //此语句用于说明程序每tick一次检查一遍有没有信号触发
pcntl_signal(SIGINT, 'signalHandler'); // 设置对应信号的回调函数
方式二: //优点性能相对高 缺点:在中间部分有sleep等类似函数无法及时执行回调函数
pcntl_signal(SIGINT, 'signalHandler'); // 设置对应信号的回调函数
//XXXX 此处是自己的业务逻辑
pcntl_signal_dispatch();函数用于在当前位置判断期间有没有触发信号,然后调用对应的回调函数
总结:
pcntl_signal函数设置信号回调函数后必须要有触发点,declare或者pcntl_signal_dispatch二选一
感谢解答
学习了!
刚开始是被 http://rango.swoole.com/archives/364 这篇文档的
这段代码在执行pcntl_signal前,先加入了declare(ticks = 1)。因为PHP的函数无法直接注册到操作系统信号设置中
这段代码误导了,一直只有declare(ticks = 1)
才会注册到操作系统信号设置中看了下,感觉文章不严谨,并非pcntl_signal性能差,而是declare(ticks = 1)性能差。
2018年的时候遇到过类似问题并对此有过一个总结,相关点分享过来:
完整总结可以去看我原贴:
http://www.blogdaren.com/post-2375.html
感谢大佬分享!但是Timer类,并没有找到代理触发了
pcntl_signal_dispatch
函数workerman的主进程和子进程对于定时器实现用的并不是同一套机制, 主进程用的是pcntl_signal相关技术,子进程则用的是event内置的相关技术,在workerman源码中我们不难发现主进程的大LOOP里明显能看到pcntl_signal_dispatch的影子。
子进程应该是通过 https://www.php.net/manual/zh/event.add.php 这个实现的
只能说默认用的是event库,也可以手动配置为调用其他网络事件库比如libevent或者swoole啥的;另不管是哪个网络事件库,对定时器实现而言都是透明的。
现在是有点想知道是怎么实现的了,哈哈
主进程使用的是pcntl_alarm做定时(秒级)
子进程event/libevent/stream_select超时 (毫秒级)
select作为事件轮训器会在每次tick的时候调用pcntl_signal_dispatch 查询有无信号需要处理
event事件轮训器不会每次调用pcntl_signal_dispatch,也就是在业务代码中 写的pcntl_signal(SIGALRM, 'signalHandler')无效
Timer类,没有找到代理触发了pcntl_signal_dispatch函数 因为在具体的事件轮训器类里面
最后这两句话我认为理解不正确,就workerman的主进程而言,其用的是alarm机制,换句话:pcntl_alarm是在给定的时间之后给当前进程发送时钟信号,同样也是需要主动调用dispatch去检测下信号的,workerman的主进程代码空间的大LOOP【见Worker::monitorWorkersForLinux()】里的dispatch就是用来干这个事情的,和具体的事件轮询类没有任何关系; 而子进程的定时器实现才会依赖到各个网络事件库。
感觉主进程是完全脱离第三方事件库的。
pcntl_alarm(1)
也就是每秒给主进程发送时钟信号循环是在等子进程的返回。同时也会检测信号,和事件轮询是有关系的,你看每次tick都在检查有无事件需要处理,可以去群里聊官方五群
是的,检测信号不只是针对主进程发送的的alarm信号,而且wait系统调用是针对子进程管理而言的,也和定时器没有什么关系; 但是主进程的定时器实现确实是没用到网络事件库的,我将一切涉及网络事件库的定时器代码已经删除并抽剥了个定时器DEMO,具体仿真代码你可以参考运行下看:
http://www.blogdaren.com/post-2643.html
不知我们是不是说的一个点
还没读到这里,子进程的这个调用等待信号的处理器怎么搞的
@张先生 话题有些许出入,其次你贴的这个看上去应该是官方自带的那个基于stream_select的定时器轮询机制,这点我认同你上面说的描述,但是子进程空间的其他网络事件库针对定时器的处理基本是没有在workerman层面直接来实现的;
@Tinywan 细节三两句不好说,简单说就是轮询,原理参考这么几个关键点理解下:
如果你是模拟官方定时器逻辑的话,这是有些问题的: