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