php使用singal為什么需要使用declare(ticks=n)語(yǔ)句?
pcntl 拓展在實(shí)現(xiàn)signal上使用了“延后執(zhí)行”的機(jī)制;因此使用該功能時(shí),必須先使用語(yǔ)句declare(ticks=1),否則注冊(cè)的singal-handel就不會(huì)執(zhí)行了
發(fā)表一下我的觀點(diǎn)
方式一://優(yōu)點(diǎn):及時(shí) 能打斷系統(tǒng)中斷 比如sleep 缺點(diǎn)性能相對(duì)低
declare(ticks = 1); //此語(yǔ)句用于說(shuō)明程序每tick一次檢查一遍有沒(méi)有信號(hào)觸發(fā)
pcntl_signal(SIGINT, 'signalHandler'); // 設(shè)置對(duì)應(yīng)信號(hào)的回調(diào)函數(shù)
方式二: //優(yōu)點(diǎn)性能相對(duì)高 缺點(diǎn):在中間部分有sleep等類似函數(shù)無(wú)法及時(shí)執(zhí)行回調(diào)函數(shù)
pcntl_signal(SIGINT, 'signalHandler'); // 設(shè)置對(duì)應(yīng)信號(hào)的回調(diào)函數(shù)
//XXXX 此處是自己的業(yè)務(wù)邏輯
pcntl_signal_dispatch();函數(shù)用于在當(dāng)前位置判斷期間有沒(méi)有觸發(fā)信號(hào),然后調(diào)用對(duì)應(yīng)的回調(diào)函數(shù)
總結(jié):
pcntl_signal函數(shù)設(shè)置信號(hào)回調(diào)函數(shù)后必須要有觸發(fā)點(diǎn),declare或者pcntl_signal_dispatch二選一
是不是由于群主使用方式二:
pcntl_signal_dispatch()
,所以應(yīng)該在業(yè)務(wù)代碼中間部分應(yīng)該避免使用sleep
等類似函數(shù)導(dǎo)致無(wú)法及時(shí)執(zhí)行回調(diào)函數(shù)?
剛開(kāi)始是被 http://rango.swoole.com/archives/364 這篇文檔的 這段代碼在執(zhí)行pcntl_signal前,先加入了declare(ticks = 1)。因?yàn)镻HP的函數(shù)無(wú)法直接注冊(cè)到操作系統(tǒng)信號(hào)設(shè)置中
這段代碼誤導(dǎo)了,一直只有declare(ticks = 1)
才會(huì)注冊(cè)到操作系統(tǒng)信號(hào)設(shè)置中
2018年的時(shí)候遇到過(guò)類似問(wèn)題并對(duì)此有過(guò)一個(gè)總結(jié),相關(guān)點(diǎn)分享過(guò)來(lái):
4、信號(hào)回調(diào)是不會(huì)自己自動(dòng)執(zhí)行的,要么主動(dòng)聲明declare(ticks=1),要么主動(dòng)調(diào)用pcntl_signal_dispatch檢查信號(hào)以執(zhí)行信號(hào)回調(diào)處理函數(shù),推薦高性能pcntl_singal_dispatch。
5、pcntl_signal_dispatch函數(shù)的作用:檢測(cè)信號(hào)隊(duì)列里是否有信號(hào)發(fā)生,如果有,則執(zhí)行進(jìn)程綁定的信號(hào)處理回調(diào)函數(shù)。
完整總結(jié)可以去看我原貼:
http://www.blogdaren.com/post-2375.html
workerman的主進(jìn)程和子進(jìn)程對(duì)于定時(shí)器實(shí)現(xiàn)用的并不是同一套機(jī)制, 主進(jìn)程用的是pcntl_signal相關(guān)技術(shù),子進(jìn)程則用的是event內(nèi)置的相關(guān)技術(shù),在workerman源碼中我們不難發(fā)現(xiàn)主進(jìn)程的大LOOP里明顯能看到pcntl_signal_dispatch的影子。
子進(jìn)程應(yīng)該是通過(guò) https://www.php.net/manual/zh/event.add.php 這個(gè)實(shí)現(xiàn)的
只能說(shuō)默認(rèn)用的是event庫(kù),也可以手動(dòng)配置為調(diào)用其他網(wǎng)絡(luò)事件庫(kù)比如libevent或者swoole啥的;另不管是哪個(gè)網(wǎng)絡(luò)事件庫(kù),對(duì)定時(shí)器實(shí)現(xiàn)而言都是透明的。
主進(jìn)程使用的是pcntl_alarm做定時(shí)(秒級(jí))
子進(jìn)程event/libevent/stream_select超時(shí) (毫秒級(jí))
select作為事件輪訓(xùn)器會(huì)在每次tick的時(shí)候調(diào)用pcntl_signal_dispatch 查詢有無(wú)信號(hào)需要處理
event事件輪訓(xùn)器不會(huì)每次調(diào)用pcntl_signal_dispatch,也就是在業(yè)務(wù)代碼中 寫的pcntl_signal(SIGALRM, 'signalHandler')無(wú)效
Timer類,沒(méi)有找到代理觸發(fā)了pcntl_signal_dispatch函數(shù) 因?yàn)樵诰唧w的事件輪訓(xùn)器類里面
最后這兩句話我認(rèn)為理解不正確,就workerman的主進(jìn)程而言,其用的是alarm機(jī)制,換句話:pcntl_alarm是在給定的時(shí)間之后給當(dāng)前進(jìn)程發(fā)送時(shí)鐘信號(hào),同樣也是需要主動(dòng)調(diào)用dispatch去檢測(cè)下信號(hào)的,workerman的主進(jìn)程代碼空間的大LOOP【見(jiàn)Worker::monitorWorkersForLinux()】里的dispatch就是用來(lái)干這個(gè)事情的,和具體的事件輪詢類沒(méi)有任何關(guān)系; 而子進(jìn)程的定時(shí)器實(shí)現(xiàn)才會(huì)依賴到各個(gè)網(wǎng)絡(luò)事件庫(kù)。
感覺(jué)主進(jìn)程是完全脫離第三方事件庫(kù)的。pcntl_alarm(1)
也就是每秒給主進(jìn)程發(fā)送時(shí)鐘信號(hào)
循環(huán)是在等子進(jìn)程的返回。同時(shí)也會(huì)檢測(cè)信號(hào),和事件輪詢是有關(guān)系的,你看每次tick都在檢查有無(wú)事件需要處理,可以去群里聊官方五群
是的,檢測(cè)信號(hào)不只是針對(duì)主進(jìn)程發(fā)送的的alarm信號(hào),而且wait系統(tǒng)調(diào)用是針對(duì)子進(jìn)程管理而言的,也和定時(shí)器沒(méi)有什么關(guān)系; 但是主進(jìn)程的定時(shí)器實(shí)現(xiàn)確實(shí)是沒(méi)用到網(wǎng)絡(luò)事件庫(kù)的,我將一切涉及網(wǎng)絡(luò)事件庫(kù)的定時(shí)器代碼已經(jīng)刪除并抽剝了個(gè)定時(shí)器DEMO,具體仿真代碼你可以參考運(yùn)行下看:
http://www.blogdaren.com/post-2643.html
不知我們是不是說(shuō)的一個(gè)點(diǎn)
@張先生 話題有些許出入,其次你貼的這個(gè)看上去應(yīng)該是官方自帶的那個(gè)基于stream_select的定時(shí)器輪詢機(jī)制,這點(diǎn)我認(rèn)同你上面說(shuō)的描述,但是子進(jìn)程空間的其他網(wǎng)絡(luò)事件庫(kù)針對(duì)定時(shí)器的處理基本是沒(méi)有在workerman層面直接來(lái)實(shí)現(xiàn)的;
@Tinywan 細(xì)節(jié)三兩句不好說(shuō),簡(jiǎn)單說(shuō)就是輪詢,原理參考這么幾個(gè)關(guān)鍵點(diǎn)理解下:
大佬們參考下下面序號(hào)是否合理?
<?php
class Tinyman
{
public static $_tasks = [];
public static function init()
{
// ① 安裝時(shí)鐘信號(hào),同時(shí)設(shè)置信號(hào)處理器 signalHandler
\pcntl_signal(\SIGALRM, array(self::class, 'signalHandler'), false);
}
public function signalHandler()
{
// ④ pcntl_signal_dispatch 捕捉信號(hào),觸發(fā)信號(hào)處理器。
// ⑤ 該鬧鐘信號(hào)同樣會(huì)被 pcntl_signal_dispatch() 信號(hào)捕捉到,然后重復(fù)觸發(fā)該信號(hào)處理器。通過(guò) kill -SIGALRM 396 發(fā)送信號(hào)量也是可以的
\pcntl_alarm(1);
// ⑥ 通過(guò) tick() 鉤子進(jìn)行業(yè)務(wù)處理
self::tick();
}
public static function tick()
{
// ⑦ 防止空任務(wù)時(shí)因繼續(xù)生產(chǎn)無(wú)效時(shí)鐘信號(hào),0:表示將不會(huì)創(chuàng)建alarm鬧鐘信號(hào)
if (empty(self::$_tasks)) {
\pcntl_alarm(0);
return;
}
// ⑧ 處理業(yè)務(wù),通過(guò)設(shè)置的 signalHandler 重復(fù)觸發(fā)該信號(hào)處理器:② 捕捉信號(hào) -> ④ 信號(hào)捕捉觸發(fā)信號(hào)處理器 -> ⑥ 通過(guò) tick() 鉤子進(jìn)行業(yè)務(wù)處理
echo ' [x] 主進(jìn)程 tick 定時(shí)任務(wù) '.json_encode(self::$_tasks), "\n";
}
}
Tinyman::init();
// ② 向當(dāng)前進(jìn)程發(fā)送 SIGALRM 信號(hào)
\pcntl_alarm(1);
$i = 0;
// 主進(jìn)程初始化,不讓當(dāng)前進(jìn)程退出
while (true) {
if ($i % 5 ==0 ) {
Tinyman::$_tasks[$i] = $i;
echo " [x] 主進(jìn)程... ".posix_getpid()." \n";
};
$i++;
sleep(1);
// ③ 捕捉信號(hào)
pcntl_signal_dispatch();
}
如果你是模擬官方定時(shí)器邏輯的話,這是有些問(wèn)題的: