PHP 並非為長駐行程設計,缺乏專用 GC 機制與有效記憶體管理,長時間運行易導致記憶體耗盡中斷。本文示範使用 pcntl 與 posix 擴展實作多進程架構,主進程負責任務分發,子進程執行具體邏輯並自動釋放資源。
| 優勢 | 說明 |
|---|---|
| 自動資源回收 | 子進程結束時,核心自動回收記憶體、檔案控制代碼等資源 |
| 故障隔離 | 子進程異常退出不影響主進程,主進程可重建任務流程 |
| 邏輯清晰 | 主進程專注調度,子進程專注業務,職責分離 |
<?php
#!/usr/bin/env php
/**
* PHP 多進程命令解析器範例
* @filename fork.php
* @author Laruence
* @license PHP License 3.0
*/
// 僅允許 CLI 模式執行
if (substr(php_sapi_name(), 0, 3) !== 'cli') {
die("This program can only be run in CLI mode\n");
}
// 移除執行時間限制(CLI 預設無限制,保險起見)
set_time_limit(0);
$pid = posix_getpid();
$user = posix_getlogin();
echo <<
pcntl_fork():建立子進程,父進程回傳子進程 PID,子進程回傳 0pcntl_wait($status, WUNTRACED):阻塞等待子進程狀態變化pcntl_wifexited() / pcntl_wexitstatus():判斷正常退出並獲取退出碼pcntl_wifsignaled() / pcntl_wtermsig():判斷信號終止並獲取信號編號posix_getpid() / posix_getlogin():獲取進程 ID 與登入用戶readline() / readline_add_history():互動式命令列輸入與歷史記錄<?php
// 生產級任務佇列雛形
class TaskWorker {
private $max_workers = 5;
private $running = [];
private $task_queue = [];
public function addTask(callable $task) {
$this->task_queue[] = $task;
}
public function run() {
while (!empty($this->task_queue) || !empty($this->running)) {
// 回收結束的子進程
foreach ($this->running as $pid => $info) {
$status = 0;
$result = pcntl_waitpid($pid, $status, WNOHANG);
if ($result > 0) {
unset($this->running[$pid]);
// 處理結果、記錄日誌...
}
}
// 啟動新任務
while (count($this->running) < $this->max_workers && !empty($this->task_queue)) {
$task = array_shift($this->task_queue);
$pid = pcntl_fork();
if ($pid == 0) {
// 子進程執行任務
try {
$task();
exit(0);
} catch (Throwable $e) {
error_log($e->getMessage());
exit(1);
}
} elseif ($pid > 0) {
$this->running[$pid] = ['start' => microtime(true)];
}
}
usleep(100000); // 避免忙等待
}
}
}
⚠️ 絕對不要在生產環境直接使用
eval()執行外部輸入! 本範例僅供學習pcntl多進程機制。實際應用應將任務封裝為獨立腳本或類方法,透過參數傳遞而非代碼注入執行。
--enable-pcntl 與 --enable-posixpcntl,需使用 pthreads 或改用進程管理工具(如 Supervisor)