《PHP實例:深入探究PHP的多進程編程方法》要點:
本文介紹了PHP實例:深入探究PHP的多進程編程方法,希望對您有用。如果有疑問,可以聯系我們。
子進程的創立
一般的子進程的寫法是:PHP實戰
<?php $pid = pcntl_fork(); if($pid == -1){ //創建失敗 die('could not fork'); } else{ if($pid){ //從這里開始寫的代碼是父進程的 exit("parent!"); } else{ //子進程代碼,為防止不絕的啟用子進程造成系統資源被耗盡的情況,一般子進程代碼運行完成后,加入exit來確保子進程正常退出. exit("child"); } } ?>
??? 上邊的代碼如果創建子進程成功的話,系統就有了2個進程,一個為父進程,一個為子進程,子進程的id號為$pid.在系統運行到$pid = pcntl_fork();時,在這個地方進行分支,父子進程各自開始運行各自的程序代碼.代碼的運行結果是parent 和child,很奇怪吧,為什么一個if和else互斥的代碼中,都輸出了結果?其實是像上邊所說的,代碼在pcntl_fork時,一個父進程運行parent,一個子進程運行了child.在代碼結果上就顯示了parent和child.至于誰先誰后的問題,這得要看系統資源的分配了.PHP實戰
??? 如果需要起多個進程來處理數據,可以根據數據的數量,依照約定好的數量比如說1000條一個進程來起子進程.使用for循環就可以了.???PHP實戰
#如果獲得的總數小于或等于0,等待60秒,并退出 if ($count <= 0) { sleep(60); exit; } #如果大于1000,計算需要起的進程數 if ($count > 1000) { $cycleSize = ceil($count/1000); } else { $cycleSize = 1; } for ($i=0; $i<$cycleSize; $i++) { $pid = pcntl_fork(); if($pid == -1) { break; } else { if($pid) { #父進程獲得子進程的pid,存入數組 $pidArr[] = $pid; } else { //開始發送,子進程執行完本身的任務后,退出. exit; } } } while(count($pidArr) > 0) { $myId = pcntl_waitpid(-1, $status, WNOHANG); foreach($pidArr as $key => $pid) { if($myId == $pid) unset($pidArr[$key]); } }
??? 然后使用crontab,來使此PHP程序每隔一段時間自動執行.PHP實戰
??? 當然,示例代碼比擬簡單,具體還需要考慮怎么防止多個子進程執行到同一條數據或者當前進程處理數據未完成時,crontab又開始執行PHP文件啟用新的進程等等.PHP實戰
PHP多進程實現方式
下面來系統地整理一下PHP多進程的實現方式:PHP實戰
1. 直接方式PHP實戰
pcntl_fork() 創建一個進程,在父進程返回值是子進程的pid,在子進程返回值是0,-1表現創建進程失敗.跟C非常相似.PHP實戰
測試腳本 test.php
?
PHP實戰
<?php // example of multiple processes date_default_timezone_set( 'Asia/Chongqing'); echo "parent start, pid ", getmypid(), "\n" ; beep(); for ($i=0; $i<3; ++$i){ $pid = pcntl_fork(); if ($pid == -1){ die ("cannot fork" ); } else if ($pid > 0){ echo "parent continue \n"; for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ echo "child start, pid ", getmypid(), "\n" ; for ($j=0; $j<5; ++$j){ beep(); } exit ; } } // *** function beep(){ echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ; sleep(1); } ?>
用命令行運行
PHP實戰
#php -f test.php
輸出成果
PHP實戰
parent start, pid 1793 1793 2013-01-14 15:04:17 parent continue 1793 2013-01-14 15:04:18 child start, pid 1794 1794 2013-01-14 15:04:18 1794 2013-01-14 15:04:19 1793 2013-01-14 15:04:19 1794 2013-01-14 15:04:20 parent continue 1793 2013-01-14 15:04:20 child start, pid 1795 1795 2013-01-14 15:04:20 17931794 2013-01-14 15:04:212013-01-14 15:04:21 1795 2013-01-14 15:04:21 1794 2013-01-14 15:04:22 1795 2013-01-14 15:04:22 parent continue 1793 2013-01-14 15:04:22 child start, pid 1796 1796 2013-01-14 15:04:22 1793 2013-01-14 15:04:23 1796 2013-01-14 15:04:23 1795 2013-01-14 15:04:23 1795 2013-01-14 15:04:24 1796 2013-01-14 15:04:24 1796 2013-01-14 15:04:25 1796 2013-01-14 15:04:26
從中看到,創建了3個子進程,和父進程一起并行運行.其中有一行格式跟其他有些分歧,
17931794??????????????? 2013-01-14 15:04:212013-01-14 15:04:21
因為兩個進程同時進行寫操作,造成了沖突.PHP實戰
2. 阻塞方式PHP實戰
用直接方式,父進程創建了子進程后,并沒有等待子進程結束,而是繼續運行.似乎這里看不到有什么問題.如果php腳本并不是運行完后自動結束,而是常駐內存的,就會造成子進程無法回收的問題.也就是僵尸進程.可以通過pcntl_wai()辦法等待進程結束,然后回收已經結束的進程.
將測試腳本改成:
?
PHP實戰
$pid = pcntl_fork(); if ($pid == -1){ ... } else if ($pid > 0){ echo "parent continue \n"; pcntl_wait($status); for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ ... }
用命令行運行
PHP實戰
#php -f test.php
輸出成果
PHP實戰
parent start, pid 1807 1807 2013-01-14 15:20:05 parent continue child start, pid 1808 1808 2013-01-14 15:20:06 1808 2013-01-14 15:20:07 1808 2013-01-14 15:20:08 1808 2013-01-14 15:20:09 1808 2013-01-14 15:20:10 1807 2013-01-14 15:20:11 1807 2013-01-14 15:20:12 parent continue child start, pid 1809 1809 2013-01-14 15:20:13 1809 2013-01-14 15:20:14 1809 2013-01-14 15:20:15 1809 2013-01-14 15:20:16 1809 2013-01-14 15:20:17 1807 2013-01-14 15:20:18 1807 2013-01-14 15:20:19 child start, pid 1810 1810 2013-01-14 15:20:20 parent continue 1810 2013-01-14 15:20:21 1810 2013-01-14 15:20:22 1810 2013-01-14 15:20:23 1810 2013-01-14 15:20:24 1807 2013-01-14 15:20:25 1807 2013-01-14 15:20:26
父進程在pcntl_wait()將本身阻塞,等待子進程運行完了才接著運行.PHP實戰
3. 非阻塞方式PHP實戰
阻塞方式失去了多進程的并行性.還有一種辦法,既可以回收已經結束的子進程,又可以并行.這就是非阻塞的方式.
修改腳本:
?
PHP實戰
<?php // example of multiple processes date_default_timezone_set( 'Asia/Chongqing'); declare (ticks = 1); pcntl_signal(SIGCHLD, "garbage" ); echo "parent start, pid ", getmypid(), "\n" ; beep(); for ($i=0; $i<3; ++$i){ $pid = pcntl_fork(); if ($pid == -1){ die ("cannot fork" ); } else if ($pid > 0){ echo "parent continue \n"; for ($k=0; $k<2; ++$k){ beep(); } } else if ($pid == 0){ echo "child start, pid ", getmypid(), "\n" ; for ($j=0; $j<5; ++$j){ beep(); } exit (0); } } // parent while (1){ // do something else sleep(5); } // *** function garbage($signal){ echo "signel $signal received\n" ; while (($pid = pcntl_waitpid(-1, $status, WNOHANG))> 0){ echo "\t child end pid $pid , status $status\n" ; } } function beep(){ echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ; sleep(1); } ?>
用命令行運行
PHP實戰
#php -f test.php &
輸出成果
PHP實戰
parent start, pid 2066 2066 2013-01-14 16:45:34 parent continue 2066 2013-01-14 16:45:35 child start, pid 2067 2067 2013-01-14 16:45:35 20662067 2013-01-14 16:45:362013-01-14 16:45:36 2067 2013-01-14 16:45:37 parent continue 2066 2013-01-14 16:45:37 child start, pid 2068 2068 2013-01-14 16:45:37 2067 2013-01-14 16:45:38 2068 2013-01-14 16:45:38 2066 2013-01-14 16:45:38 parent continue 2066 2013-01-14 16:45:40 child start, pid 2069 2069 2067 2013-01-14 16:45:40 2013-01-14 16:45:40 2068 2013-01-14 16:45:40 2066 2013-01-14 16:45:41 2069 2013-01-14 16:45:41 2068 2013-01-14 16:45:41 signel 17 received child end pid 2067, status 0 2069 2013-01-14 16:45:42 2068 2013-01-14 16:45:42 2069 2013-01-14 16:45:43 signel 17 received child end pid 2068, status 0 2069 2013-01-14 16:45:44 signel 17 received child end pid 2069, status 0
多個進程又并行運行了,并且運行大約10秒鐘之后,用 ps -ef | grep php 查看正在運行的進程,只有一個進程
lqling??? 2066? 1388? 0 16:45 pts/1??? 00:00:00 php -f t5.php
是父進程,子進程被回收了.PHP實戰
子進程退出狀態PHP實戰
pcntl_waitpid(-1, $status, WNOHANG) $status
?返回子進程的結束狀態PHP實戰
windows下多線程PHP實戰
windows系統不支持pcntl函數,幸好有curl_multi_exec()這個工具,利用內部的多線程,拜訪多個鏈接,每個鏈接可以作為一個任務.PHP實戰
編寫腳本 test1.php
?
PHP實戰
<?php date_default_timezone_set( 'Asia/Chongqing'); $tasks = array( 'http://localhost/feedbowl/t2.php?job=task1', 'http://localhost/feedbowl/t2.php?job=task2', 'http://localhost/feedbowl/t2.php?job=task3' ); $mh = curl_multi_init(); foreach ($tasks as $i => $task){ $ch[$i] = curl_init(); curl_setopt($ch[$i], CURLOPT_URL, $task); curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1); curl_multi_add_handle($mh, $ch[$i]); } do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); while ($active && $mrc == CURLM_OK) { if (curl_multi_select($mh) != -1) { do {$mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM); } } // completed, checkout result foreach ($tasks as $j => $task){ if (curl_error($ch[$j])){ echo "task ${j} [$task ] error " , curl_error($ch[$j]), "\r\n" ; } else { echo "task ${j} [$task ] get: \r\n" , curl_multi_getcontent($ch[$j]), "\r\n" ; } } ?>
編寫腳本 test2.php
?
PHP實戰
<?php date_default_timezone_set( 'Asia/Chongqing'); echo "child start, pid ", getmypid(), "\r\n" ; for ($i=0; $i<5; ++$i){ beep(); } exit (0); // *** function beep(){ echo getmypid(), "\t" , date('Y-m-d H:i:s' , time()), "\r\n"; sleep(1); } ?>
用命令行運行
PHP實戰
#php -f test1.php &
輸出成果
PHP實戰
task 0 [http://localhost/feedbowl/t2.php?job=task1] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 1 [http://localhost/feedbowl/t2.php?job=task2] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39 task 2 [http://localhost/feedbowl/t2.php?job=task3] get: child start, pid 5804 5804 2013-01-15 20:22:35 5804 2013-01-15 20:22:36 5804 2013-01-15 20:22:37 5804 2013-01-15 20:22:38 5804 2013-01-15 20:22:39
從打印的光陰看到,多個任務幾乎是同時運行的.
PHP實戰
維易PHP培訓學院每天發布《PHP實例:深入探究PHP的多進程編程方法》等實戰技能,PHP、MYSQL、LINUX、APP、JS,CSS全面培養人才。
轉載請注明本頁網址:
http://www.snjht.com/jiaocheng/8786.html