pcntl 擴展)。pcntl 擴展:pecl install pcntl 或原始碼編譯 --enable-pcntl。$pid = pcntl_fork();
if ($pid === -1) { die('fork 失敗'); }
elseif ($pid > 0) { /* 父進程:繼續派發任務或等待 */ }
else { /* 子進程:執行具體下載任務 */ }
<?php
// download_images.php
$url = 'https://example.com/gallery';
$html = file_get_contents($url);
preg_match_all('/
$imgUrl) {
$pid = pcntl_fork();
if ($pid === -1) { echo "fork 失敗\n"; continue; }
if ($pid === 0) {
// 子進程
download_one($url, $imgUrl);
exit(0); // 關鍵:子進程必須 exit,否則會繼續迴圈產生孫進程
}
$pids[] = $pid;
}
// 父進程等待所有子進程結束
foreach ($pids as $pid) {
pcntl_waitpid($pid, $status);
}
echo "下載完成,耗時 " . round(microtime(true)-$start,2) . " 秒\n";
function download_one($base, $src) {
// 處理相對/絕對路徑
if (strpos($src, '//') === 0) $src = 'https:'.$src;
elseif (!preg_match('#^https?://#', $src)) $src = rtrim($base,'/').'/'.ltrim($src,'/');
$data = @file_get_contents($src);
if ($data === false) return;
$fname = basename(parse_url($src, PHP_URL_PATH));
$fname = preg_replace('/[^a-zA-Z0-9._-]/','_',$fname);
$path = sys_get_temp_dir().'/img_dl/'.$fname;
@mkdir(dirname($path), 0777, true);
file_put_contents($path, $data);
echo "[$$] saved $fname\n";
}
| 點 | 說明 |
|---|---|
子進程 exit(0) | 避免子進程繼續執行父進程迴圈,產生指數級進程爆炸 |
父進程 pcntl_waitpid | 回收子進程資源,避免殭屍進程;可加 WNOHANG 非阻塞輪詢 |
| 錯誤處理 | 下載失敗靜默返回,不影響其他子進程 |
| 並發控制 | 大量圖片時建議加入 semaphore 或 proc_open 池限制同時進程數(如 10-20) |
| 信號處理 | 註冊 pcntl_signal(SIGTERM, ...) 優雅關閉 |
pcntl_fork 適合快速腳本、無依賴環境;長期專案建議遷移至協程/非阻塞架構。