嘗試php命令行腳本多進程並發執行


php不支持多線程,但是我們可以把問題轉換成“多進程”來解決。由於php中的pcntl_fork只有unix平台才可以使用,所以本文嘗試使用popen來替代。 
 
下面是一個例子:
  
被並行調用的子程序:

  1. <?php 
  2. if($argc==1){ 
  3.          echo("argv\n"); 
  4. $arg = $argv[1];     
  5. for($i=0; $i<10; $i++) 
  6. echo($i.".1.".time()." exec $arg \n"); 
  7. if($arg=='php2') 
  8. sleep(1); 
  9. echo($i.".2.".time()." exec $arg \n"); 
  10. sleep(1); 
  11. }else{ 
  12. sleep(1); 
  13. ?> 

----------------------------
主調用者程序,由他調用子進程,同時並發的收集子程序的輸出 

 

  1. <?php 
  2. error_reporting(E_ALL); 
  3.      
  4. $handle1 = popen('php sub.php php1', 'r'); 
  5. $handle2 = popen('php sub.php php2', 'r'); 
  6. $handle3 = popen('php sub.php php3', 'r'); 
  7.      
  8. echo "'$handle1'; " . gettype($handle1) . "\n"; 
  9. echo "'$handle2'; " . gettype($handle2) . "\n"; 
  10. echo "'$handle3'; " . gettype($handle3) . "\n"; 
  11.       
  12. //sleep(20); 
  13. while(!feof($handle1) || !feof($handle2) || !feof($handle3) ){ 
  14. $read = fgets($handle1); 
  15. echo $read; 
  16.       
  17. $read = fgets($handle2); 
  18. echo $read; 
  19.       
  20. $read = fgets($handle3); 
  21. echo $read; 
  22.  
  23.  
  24. pclose($handle1); 
  25. pclose($handle2); 
  26. pclose($handle3); 
  27. ?> 

-------------------

下面是我機器上的輸出:


> php exec.php
'Resource id #4'; resource
'Resource id #5'; resource
'Resource id #6'; resource
0.1.1147935331 exec php1
0.1.1147935331 exec php2
0.1.1147935331 exec php3
1.1.1147935332 exec php1
0.2.1147935332 exec php2
1.1.1147935332 exec php3
2.1.1147935333 exec php1
1.1.1147935333 exec php2
2.1.1147935333 exec php3
3.1.1147935334 exec php1
1.2.1147935334 exec php2
3.1.1147935334 exec php3
4.1.1147935335 exec php1
2.1.1147935335 exec php2
4.1.1147935335 exec php3
5.1.1147935336 exec php1
2.2.1147935336 exec php2
5.1.1147935336 exec php3
6.1.1147935337 exec php1
3.1.1147935337 exec php2
6.1.1147935337 exec php3
7.1.1147935338 exec php1
3.2.1147935338 exec php2
7.1.1147935338 exec php3
8.1.1147935339 exec php1
4.1.1147935339 exec php2
8.1.1147935339 exec php3
9.1.1147935340 exec php1
4.2.1147935340 exec php2
9.1.1147935340 exec php3
5.1.1147935341 exec php2
5.2.1147935342 exec php2
6.1.1147935343 exec php2
6.2.1147935344 exec php2
7.1.1147935345 exec php2
7.2.1147935346 exec php2
8.1.1147935347 exec php2
8.2.1147935348 exec php2
9.1.1147935349 exec php2
9.2.1147935350 exec php2

**總結:**

**主程序循環等待子進程, 通過fgets或fread 把子進程的輸出獲取出來 , 從時間戳上看,的確實現了並發執行。**
  
-----------------------------------------------
改進:
  
*  popen打開的句柄是單向的,如果需要向子進程交互,可以使用proc_open
*  使用數組和子函數代替while(!feof($handle1) || !feof($handle2) || !feof($handle3) )這種齷齪的寫法
*  用fread一次把子進程已經產生的輸出取完,而不是每次一行。


這是另一個改進:
一個並發執行shell任務的調度者,本程序讀取一個任務文件,把里面的每行命令並發執行, 可以設置同時存在的子進程數目:

  1. <? 
  2. /* 
  3.          主任務管理器 
  4.          並發的執行子任務列表 
  5. */ 
  6.  
  7. include("../common/conf.php"); 
  8. include("../common/function.php"); 
  9.  
  10. //開啟的進程數 
  11. $exec_number = 40 ; 
  12.  
  13. /***** main ********/ 
  14. if($argc==1){ 
  15.          echo("argv\n"); 
  16. $taskfile = $argv[1]; 
  17.  
  18.  
  19. //tasklist 
  20. $tasklist = file($taskfile); 
  21.  
  22.  
  23. $tasklist_len = count($tasklist); 
  24. $tasklist_pos = 0; 
  25.  
  26.  
  27. $handle_list = array(); 
  28.  
  29.  
  30. while(1){ 
  31.  
  32.  
  33.          //子進程列表有空閑,則填充補齊子進程列表 
  34.          if($exec_number > count($handle_list) && 
  35.                              $tasklist_pos < $tasklist_len) 
  36.          { 
  37.                  for($i=$tasklist_pos; $i<$tasklist_len; ) 
  38.                      { 
  39.                              $command = $tasklist[$i] ; 
  40.                              $handle_list[] = popen($command , "r" ); 
  41.                              tolog("begin task \t ".$tasklist[$i]); 
  42.                              $i++; 
  43.                              if($exec_number == count($handle_list)) break; 
  44.                      } 
  45.                      $tasklist_pos = $i; 
  46.          } 
  47.  
  48.  
  49.          //如果子進程列表空,退出 
  50.          if(0 == count($handle_list)) 
  51.          { 
  52.                      break; 
  53.          } 
  54.  
  55.  
  56.          //檢查子進程列表的輸出,把停掉的子進程關閉並記錄下來 
  57.          $end_handle_keys = array(); 
  58.          foreach($handle_list as $key => $handle) 
  59.          { 
  60.                      //$str = fgets($handle, 65536); 
  61.                      $str = fread($handle, 65536); 
  62.                      echo($str); 
  63.  
  64.  
  65.                      if(feof($handle)) 
  66.                      { 
  67.                              $end_handle_keys[] = $key; 
  68.                              pclose($handle); 
  69.                      } 
  70.          } 
  71.  
  72.  
  73.          //踢出停掉的子進程 
  74.          foreach($end_handle_keys as $key) 
  75.          { 
  76.                      unset($handle_list[$key]); 
  77.                      //var_dump($handle_list); 
  78.                      //exit; 
  79.          } 
  80.  
  81.  
  82.  
  83.  
  84. tolog("\n\n*******************end**********************\n\n", "" ,     true); 
  85. ?>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM