Корректное завершение
Останавливайте фоновую работу чисто: ждите с дедлайном, просите задачу завершиться и
применяйте силу только тогда, когда она не слушается. Сигналы требуют расширения posix.
Ожидание с таймаутом
По умолчанию join() блокируется навсегда. Передайте таймаут (в секундах), чтобы ограничить
ожидание — он вернёт null, если дедлайн наступит раньше, чем задача завершится:
$exitCode = $thread->join(timeout: 30);
if ($exitCode === null) {
// Всё ещё выполняется спустя 30 с — решаем, что делать дальше
echo "Timed out; asking it to stop...\n";
} elseif ($exitCode === 0) {
echo "Finished cleanly.\n";
} else {
echo "Failed with exit code {$exitCode}.\n";
}Возвращаемые значения join()
int — код выхода при завершении · null — при таймауте · -1, если thread не
запускался. Полная таблица — в справочнике API.
Сначала по-хорошему, потом силой
Паттерн чистого завершения: отправьте SIGTERM (перехватываемый, даёт задаче прибраться),
дайте немного времени и переходите к SIGKILL (неперехватываемый) только если вас
проигнорировали:
$thread->terminate(); // SIGTERM — просьба завершиться
if ($thread->join(timeout: 5) === null) {
echo "Ignored SIGTERM; killing.\n";
$thread->kill(); // SIGKILL — нельзя перехватить или проигнорировать
$thread->join();
}| Метод | Сигнал | Перехватывается? | Когда использовать |
|---|---|---|---|
interrupt() |
SIGINT |
да | Прерывание в стиле Ctrl+C |
terminate() |
SIGTERM |
да | Просьба о чистом завершении |
kill() |
SIGKILL |
нет | Крайняя мера после terminate() |
Обработайте SIGTERM внутри задачи
Чтобы terminate() был корректным, задача должна слушать сигнал и останавливаться в
безопасной точке:
<?php
use Flytachi\Winter\Thread\Runnable;
class BatchJob implements Runnable
{
public function run(array $args): void
{
pcntl_async_signals(true);
$stop = false;
pcntl_signal(SIGTERM, function () use (&$stop) {
$stop = true; // не делайте тяжёлую работу в обработчике сигнала
});
foreach ($this->chunks() as $chunk) {
if ($stop) {
$this->checkpoint(); // сохраняем прогресс и выходим чисто
return;
}
$this->process($chunk);
}
}
}Пауза и возобновление
Нужно приостановить процесс, не завершая его? pause() отправляет SIGSTOP (ОС замораживает
процесс; он остаётся в таблице процессов, поэтому isAlive() по-прежнему true), а
resume() отправляет SIGCONT:
$thread->pause(); // заморожен ОС — нельзя перехватить
// ... освобождаем CPU под другое ...
$thread->resume(); // продолжает с того же местаСвязанное
- Справочник API — все методы сигналов
- Сигналы и коды выхода — полная таблица сигналов
- Переиспользование PID и сигналы — почему слать сигналы по «сырому» PID рискованно