2024 年 PHP Conference Japan

pcntl_alarm

(PHP 4 >= 4.3.0, PHP 5, PHP 7, PHP 8)

pcntl_alarm設定傳送訊號的鬧鐘

說明

pcntl_alarm(int $seconds): int

建立一個計時器,在指定的秒數後,會向行程發送 SIGALRM 訊號。任何呼叫 pcntl_alarm() 都將會取消先前設定的鬧鐘。

參數

seconds

等待的秒數。如果 seconds 為零,則不會建立新的鬧鐘。

返回值

返回任何先前排程的鬧鐘在傳送之前剩餘的秒數,如果沒有先前排程的鬧鐘,則返回 0

新增筆記

使用者貢獻的筆記 4 則筆記

kuba at valentine dot dev
4 年前
這就是您夢寐以求的通用逾時功能,而且它非常可靠,基本上是無懈可擊的。它可以中斷您提交的任何操作,例如 socket_connect()、socket_read()、fread()、無限 while() 迴圈、sleep()、semaphore 等等——任何阻擋操作都沒問題。您可以指定自己的處理器,輕鬆解決任何通常會導致程式碼沒有回應的情況。

<?php
/**
* 因為我們不應該以同步方式處理非同步
* 事件。
*/
pcntl_async_signals(TRUE);

/**
* 我們可以更改的一些旗標,以確定
* 我們的操作已逾時。
*/
$timed_out = FALSE;

/**
* 註冊 SIGALRM 訊號處理器,以避免
* 在訊號到達時程序被終止。
*/
pcntl_signal(SIGALRM, function($signal) use (&$timed_out) {
$timed_out = TRUE;
});

/**
* 現在我們將逾時設定為 2 秒,但這不是一成不變的
* 我們可以隨時呼叫 pcntl_alarm() 來延長或關閉它。
*/
pcntl_alarm(2);

/**
* 在這裡,我們會執行一些結果不可預測的操作,可能會
* 讓我們的程式長時間阻塞。
* 我喜歡用 sockets 作為例子,但它可以是任何東西。
*/
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
$connection = socket_connect($socket, 'irc.ircnet.com', 6667);

/**
* 如果我們的阻塞操作沒有逾時,那麼
* 計時器仍在運作,我們應該盡快將其關閉。
*/
$timed_out || pcntl_alarm(0);

/**
* 現在我們可以做任何我們想做的事情。
*/
$status = $connection ? '已連線。' : ($timed_out ? '已逾時。' : socket_strerror(socket_last_error($socket)));
echo
'狀態:'. $status . PHP_EOL;
kuba at valentine dot dev
1 年前
請記住,您可以輕鬆且有選擇地阻止您選擇的信號類型(包括 SIGALRM)中斷您不希望被中斷的阻塞操作。 使用 pcntl_sigprocmask() 在需要時屏蔽您的任何程式碼區塊,而不會影響其餘程式碼的預期信號行為。

以下是一個簡單的例子,說明如何屏蔽一個阻塞的 sleep 操作不被中斷,同時允許另一個阻塞的 sleep 操作被中斷 - 它們彼此相鄰。

<?php
$alarm
= 2;
$sleep = 10;
$stamp = time();

pcntl_async_signals(TRUE);

/**
* Alarm signal handler.
*/
pcntl_signal(SIGALRM, function(int $sig) use ($alarm, &$stamp) {
$late = (($now = time()) - $stamp) - $alarm;
$stamp = $now;
echo
'* ALARM SIGNAL HANDLER * Fired '. ($late ? ($late .' seconds late') : 'on schedule') .' *'. PHP_EOL;
pcntl_alarm($alarm);
});

/**
* Sleeping function.
*/
function get_some_sleep(int $duration, string $info) {
$start = time();
echo
PHP_EOL . $duration .' seconds sleep - '. $info . PHP_EOL;
sleep($duration);
$early = $duration - (time() - $start);
echo
'Sleep was '. ($early ? ('interrupted. Woke up '. $early .' seconds early.') : 'uninterrupted.') . PHP_EOL;
}

/**
* Here we schedule first alarm.
*/
pcntl_alarm($alarm);

while (
TRUE) {
/**
* This sleep can and will be interrupted by alarm signal.
*/
get_some_sleep($sleep, 'without a shield');

/**
* Now we set our shield. From now on blocked signals will be ignored.
* Each blocked signal type that fires while shield is up is put on hold.
*/
pcntl_sigprocmask(SIG_BLOCK, [SIGALRM]);
get_some_sleep($sleep, 'protected by sigprocmask()');

/**
* Now we take off our shield. If any blocked signal fired while shield
* was up now it's handler will be executed immediately, single time.
* This is very useful behaviour because we protect what needs protecting but also
* don't miss out on anything - without being flooded thanks to "once per type" queue.
*/
pcntl_sigprocmask(SIG_UNBLOCK, [SIGALRM]);
}
molsavsky1 at gmail dot com
2 年前
請注意,pcntl_signal 會中斷 (u)sleep 呼叫,一旦處理程式完成,這些呼叫將不會恢復。

這是一個已記錄的行為(https://php.dev.org.tw/manual/en/function.sleep.php),儘管第一次遇到時可能看起來像個錯誤。

來自文件
「如果呼叫被信號中斷,sleep() 會返回一個非零值。在 Windows 上,此值將始終為 192(Windows API 中 WAIT_IO_COMPLETION 常數的值)。在其他平台上,返回值將是剩餘的睡眠秒數。」

```
<?php

$interval
= 1;
pcntl_async_signals(true);

pcntl_signal(SIGALRM, function () use ($interval): void {
echo
'SIGALRM 呼叫了' . PHP_EOL;
pcntl_alarm($interval);
});

pcntl_alarm($interval);

echo
'睡眠(將被中斷)開始' . PHP_EOL;

sleep(100000000000);

echo
'睡眠因中斷而提前結束' . PHP_EOL;

$sleepTimeout = 10;
echo
"正確睡眠 {$sleepTimeout} 秒" . PHP_EOL;

$startedAt = time();
while (
$sleepTimeout > 0 && ($sleepTimeout = sleep($sleepTimeout)) !== true) {
echo
"睡眠中斷,還剩 {$sleepTimeout} 秒" . PHP_EOL;
}

$elapsed = time() - $startedAt;
echo
"睡眠在 {$elapsed} 秒後結束" . PHP_EOL;
```
Gao,Shengwei
6 年前
使用 pcntl_signal_dispatch() 來捕捉信號,不要使用 declare(ticks=1) 因為它效率低下

<?php
pcntl_signal
(SIGALRM, function () {
echo
'收到鬧鐘訊號!' . PHP_EOL;
},
false);

pcntl_alarm(5);

while (
true) {
pcntl_signal_dispatch();
sleep(1);
}
To Top