php實現多進程、多線程


  孤兒進程:一個父進程退出,而它的一個或多個子進程還在運行,那么那些子進程將成為孤兒進程。孤兒進程將被init進程(進程號為1)所收養,並由init進程對它們完成狀態收集工作。

  僵屍進程:一個進程使用fork創建子進程,如果子進程退出,而父進程並沒有調用wait或waitpid獲取子進程的狀態信息,那么子進程的進程描述符仍然保存在系統中。這種進程稱之為僵死進程。

  僵屍進程危害:如果進程不調用wait / waitpid的話, 那么保留的那段信息就不會釋放,其進程號就會一直被占用,但是系統所能使用的進程號是有限的,如果大量的產生僵死進程,將因為沒有可用的進程號而導致系統不能產生新的進程. 此即為僵屍進程的危害,應當避免。任何一個子進程(init除外)在exit()之后,並非馬上就消失掉,而是留下一個稱為僵屍進程(Zombie)的數據結構,等待父進程處理。

  已經產生的僵屍進程,解決方法:kill掉父進程,它產生的僵死進程就變成了孤兒進 程,這些孤兒進程會被init進程接管,init進程會wait()這些孤兒進程,釋放它們占用的系統進程表中的資源。

  僵屍進程解決辦法

  (1)通過信號機制

    子進程退出時向父進程發送SIGCHILD信號,父進程處理SIGCHILD信號。在信號處理函數中調用wait進行處理僵屍進程。

  (2)fork兩次
    《Unix 環境高級編程》8.6節說的非常詳細。原理是將子進程成為孤兒進程,從而其的父進程變為init進程,通過init進程可以處理僵屍進程。

 

 

 

多進程與多線程比較

對比維度

多進程

多線程

總結

數據共享、同步

數據共享復雜,需要用IPC;數據是分開的,同步簡單

因為共享進程數據,數據共享簡單,但也是因為這個原因導致同步復雜

各有優勢

內存、CPU

占用內存多,切換復雜,CPU利用率低

占用內存少,切換簡單,CPU利用率高

線程占優

創建銷毀、切換

創建銷毀、切換復雜,速度慢

創建銷毀、切換簡單,速度很快

線程占優

編程、調試

編程簡單,調試簡單

編程復雜,調試復雜

進程占優

可靠性

進程間不會互相影響

一個線程掛掉將導致整個進程掛掉

進程占優

分布式

適應於多核、多機分布式;如果一台機器不夠,擴展到多台機器比較簡單

適應於多核分布式

進程占優

 

1)需要頻繁創建銷毀的優先用線程

原因請看上面的對比。

這種原則最常見的應用就是Web服務器了,來一個連接建立一個線程,斷了就銷毀線程,要是用進程,創建和銷毀的代價是很難承受的

2)需要進行大量計算的優先使用線程

所謂大量計算,當然就是要耗費很多CPU,切換頻繁了,這種情況下線程是最合適的。

這種原則最常見的是圖像處理、算法處理。

3)強相關的處理用線程,弱相關的處理用進程

什么叫強相關、弱相關?理論上很難定義,給個簡單的例子就明白了。

一般的Server需要完成如下任務:消息收發、消息處理。“消息收發”和“消息處理”就是弱相關的任務,而“消息處理”里面可能又分為“消息解碼”、“業務處理”,這兩個任務相對來說相關性就要強多了。因此“消息收發”和“消息處理”可以分進程設計,“消息解碼”、“業務處理”可以分線程設計。

當然這種划分方式不是一成不變的,也可以根據實際情況進行調整。

4)可能要擴展到多機分布的用進程,多核分布的用線程

原因請看上面對比。

5)都滿足需求的情況下,用你最熟悉、最拿手的方式

至於“數據共享、同步”、“編程、調試”、“可靠性”這幾個維度的所謂的“復雜、簡單”應該怎么取舍,我只能說:沒有明確的選擇方法。但我可以告訴你一個選擇原則:如果多進程和多線程都能夠滿足要求,那么選擇你最熟悉、最拿手的那個。 

需要提醒的是:雖然我給了這么多的選擇原則,但實際應用中基本上都是“進程+線程”的結合方式,千萬不要真的陷入一種非此即彼的誤區。

 

消耗資源:

從內核的觀點看,進程的目的就是擔當分配系統資源(CPU時間、內存等)的基本單位。線程是進程的一個執行流,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位。

線程,它們彼此之間使用相同的地址空間,共享大部分數據,啟動一個線程所花費的空間遠遠小於啟動一個進程所花費的空間,而且,線程間彼此切換所需的時間也遠遠小於進程間切換所需要的時間。據統計,總的說來,一個進程的開銷大約是一個線程開銷的30倍左右,當然,在具體的系統上,這個數據可能會有較大的區別。

通訊方式:

進程之間傳遞數據只能是通過通訊的方式,即費時又不方便。線程時間數據大部分共享(線程函數內部不共享),快捷方便。但是數據同步需要鎖對於static變量尤其注意

線程自身優勢:

提高應用程序響應;使多CPU系統更加有效。操作系統會保證當線程數不大於CPU數目時,不同的線程運行於不同的CPU上;

改善程序結構。一個既長又復雜的進程可以考慮分為多個線程,成為幾個獨立或半獨立的運行部分,這樣的程序會利於理解和修改。

 

1. [代碼]PHP實現多進程並行操作(可做守護進程) 

 1 /**
 2  * 入口函數
 3  * 將此文件保存為 ProcessOpera.php
 4  * 在terminal中運行 /usr/local/php/bin/php ProcessOpera.php & 
 5  * 查看進程 ps aux|grep php
 6  */
 7  
 8  
 9 ProcessOpera("runCode", array(), 8);
10  
11 /**
12  * run Code
13  */
14 function runCode($opt = array()) {
15    //需要在守護進程中運行的代碼
16 }
17  
18 /**
19  * $func為子進程執行具體事物的函數名稱
20  * $opt為$func的參數 數組形式
21  * $pNum 為fork的子進程數量
22  */
23 function ProcessOpera($func, $opts = array(), $pNum = 1) {
24     while(true) {
25         $pid = pcntl_fork();
26         if($pid == -1) {
27             exit("pid fork error");
28         }   
29         if($pid) {
30             static $execute = 0;
31             $execute++;
32             if($execute >= $pNum) {
33                 pcntl_wait($status);
34                 $execute--;
35             }   
36         } else {
37             while(true) {
38                 //somecode
39                 $func($opts);
40                 sleep(1);
41             }   
42             exit(0);
43         }   
44     }   
45 }

 

2. [代碼]PHP實現多線程操作 

 1 class My extends Thread {
 2     protected $name;
 3     public $runing;
 4     function __construct($name){
 5         $this->runing=1;
 6         $this->param=0;
 7         $this->name=$name;
 8     }
 9     public function run() {
10         while($this->runing){
11             if($this->param){
12                 $time=rand(1,5);
13                 echo 'I am thread '.$this->name.',pid: '.$this->getCreatorId().",param: {$this->param},need {$time}s\n"; 
14                 sleep($time);
15                 $this->param=0;
16             }else{
17                 echo "Thread {$this->name} waiting...\n";
18             }
19             sleep(1);
20         }
21     }
22 }
23 $pool=array();
24 $pool[]=new My('a');
25 $pool[]=new My('b');
26 $pool[]=new My('c');
27 //開啟所有線程
28 foreach ($pool as $w) {
29     $w->start();
30 }
31 //派發任務
32 unset($w);
33 for($i=1;$i<10;$i++){
34     $woker_content=$i;
35     while(1){
36         foreach($pool as $w){
37             if(!$w->param){
38                 $w->param=$woker_content;
39                 echo "Thread {$w->name} empty,put param {$woker_content}.\n";
40                 break 2;
41             }
42         }
43         sleep(1);    
44     }
45 }
46 
47 unset($w);
48 while(count($pool)){
49     foreach ($pool as $k => $w) {
50         if(!$w->param){
51             $w->runing=false;
52             unset($pool[$k]);
53             echo "Thread {$w->name} end,exit!\n";
54         }
55     }
56     sleep(1);
57 }
58 
59 echo 'All thread end!';

 


免責聲明!

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



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