PHP之pcntl_fork多進程併發編程示例

    待下載的網頁地址放在$urls數組中,按指定的併發數多進程下載網頁,下載的網頁保存在本地硬盤,下載的網頁大小通過linux消息隊列發送給父進程累加,全部網頁下載完成後,父進程顯示下載的網頁數、字節數。代碼如下。

<?
//$urls數組用於保存要下載的網址,實際應用中一般從文件或數據庫中讀取網址保存到$urls中。
$urls = array('http://www.qq.com','http://www.sohu.com','http://www.sina.com.cn',....);
$urls_num = count($urls);//數組大小,也是網址數量
$msg_file = "/tmp/download_msgqueue.txt";//下面3行創建linux消息隊列下,該文件須先創建好
$msg_queuekey = ftok($msg_file,'R');//touch /tmp/download_msgqueue.txt
$msg_queue = msg_get_queue($msg_queuekey, 0666); 
$maxtasknum = 5;//設定併發進程數
$ct = 0;//$urls數組用的計數器
$cttask = 0;//併發進程計數器
$pids = array();//保存進程的數組
$total_bytes = 0;//下載網頁的字節數
while ($ct<$urls_num) {//循環抓取$urls數組中指定的網頁
while ($cttask<$maxtasknum && $ctproc<$urls_num) {//fork出指定的併發數進程
$pids[$ct] = pcntl_fork();
if ($pids[$ct]==-1) {
echo "create subproc fail.\n";
exit(0);
}
elseif ($pids[$ct]>0) {//父進程
}
elseif ($pids[$ct]==0) {//子進程
download($urls[$ct], $msg_queue);
exit(0);
}
$cttask++;
$ct++;
}
$tmppid = pcntl_waitpid(0, $status);//等待子進程結束
foreach($pids as $key => $pid) {
    if($tmppid == $pid){
    unset($pids[$key]);
    $cttask--;//子進程結束後,併發進程計數器減1
    }
}
do {//從消息隊列出取出每個網頁的大小,計算下載的字節數。如果要下載的網頁很多,需要把此段代碼放到下載網頁的循環中,否則可能會出現隊列滿的情況。
msg_receive($msg_queue, 0, $message_type, 16, $message, true, MSG_IPC_NOWAIT); 
//echo "[".$message."]\n";
$total_bytes += $message;
$a = msg_stat_queue($msg_queue);
if($a['msg_qnum'] == 0){//這種方式退出比$ct==$urls_num好,因爲如果fork==-1,就不會有$urls_num個消息,程序會永遠等待消息。
    break;
}
} while(true);
}
while ($cttask > 0) {//等待最後$cttask個子進程結束
$tmppid = pcntl_waitpid(0,$status);
foreach($pids as $key => $pid) {
    if($tmppid == $pid){
    unset($pids[$key]);
    $cttask--;
    }
}
}
do {//取得最後$cttask個子進程的消息
msg_receive($msg_queue, 0, $message_type, 16, $message, true, MSG_IPC_NOWAIT); 
//echo "[".$message."]\n";
$total_bytes += $message;
$a = msg_stat_queue($msg_queue);
if($a['msg_qnum'] == 0){
    break;
}
} while(true);
msg_remove_queue($msg_queue);//刪除消息隊列
echo "\nDone. download: ".$urls_num." pages,total: ".round($total_bytes/1024,3)." KB \n";
exit(0);
function download($url, $msg_queue) {//下載指定網頁,把內容保存在本地硬盤,並下載內容的長度放入消息隊列中
$dirname = "/tmp/donwload/";//保存下載網頁的目錄,要事先創建好
$content = file_get_contents($url);
if ($content === false) {
$content = 0;
}
$url_parts = parse_url($url);
$fname = $dirname.$url_parts['host'];
$ret = file_put_contents($fname, $content);
msg_send($msg_queue, 1, strlen($content));
} 
?>

參考資料:

PHP實現進程間通信:消息隊列 https://www.douban.com/note/245520545/


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章