php实现多进程、多线程
孤兒進(jìn)程:一個(gè)父進(jìn)程退出,而它的一個(gè)或多個(gè)子進(jìn)程還在運(yùn)行,那么那些子進(jìn)程將成為孤兒進(jìn)程。孤兒進(jìn)程將被init進(jìn)程(進(jìn)程號為1)所收養(yǎng),并由init進(jìn)程對它們完成狀態(tài)收集工作。
僵尸進(jìn)程:一個(gè)進(jìn)程使用fork創(chuàng)建子進(jìn)程,如果子進(jìn)程退出,而父進(jìn)程并沒有調(diào)用wait或waitpid獲取子進(jìn)程的狀態(tài)信息,那么子進(jìn)程的進(jìn)程描述符仍然保存在系統(tǒng)中。這種進(jìn)程稱之為僵死進(jìn)程。
僵尸進(jìn)程危害:如果進(jìn)程不調(diào)用wait / waitpid的話,?那么保留的那段信息就不會(huì)釋放,其進(jìn)程號就會(huì)一直被占用,但是系統(tǒng)所能使用的進(jìn)程號是有限的,如果大量的產(chǎn)生僵死進(jìn)程,將因?yàn)闆]有可用的進(jìn)程號而導(dǎo)致系統(tǒng)不能產(chǎn)生新的進(jìn)程. 此即為僵尸進(jìn)程的危害,應(yīng)當(dāng)避免。任何一個(gè)子進(jìn)程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個(gè)稱為僵尸進(jìn)程(Zombie)的數(shù)據(jù)結(jié)構(gòu),等待父進(jìn)程處理。
已經(jīng)產(chǎn)生的僵尸進(jìn)程,解決方法:kill掉父進(jìn)程,它產(chǎn)生的僵死進(jìn)程就變成了孤兒進(jìn) 程,這些孤兒進(jìn)程會(huì)被init進(jìn)程接管,init進(jìn)程會(huì)wait()這些孤兒進(jìn)程,釋放它們占用的系統(tǒng)進(jìn)程表中的資源。
僵尸進(jìn)程解決辦法
(1)通過信號機(jī)制
子進(jìn)程退出時(shí)向父進(jìn)程發(fā)送SIGCHILD信號,父進(jìn)程處理SIGCHILD信號。在信號處理函數(shù)中調(diào)用wait進(jìn)行處理僵尸進(jìn)程。
(2)fork兩次
《Unix 環(huán)境高級編程》8.6節(jié)說的非常詳細(xì)。原理是將子進(jìn)程成為孤兒進(jìn)程,從而其的父進(jìn)程變?yōu)閕nit進(jìn)程,通過init進(jìn)程可以處理僵尸進(jìn)程。
?
?
?
多進(jìn)程與多線程比較
| 對比維度 | 多進(jìn)程 | 多線程 | 總結(jié) |
| 數(shù)據(jù)共享、同步 | 數(shù)據(jù)共享復(fù)雜,需要用IPC;數(shù)據(jù)是分開的,同步簡單 | 因?yàn)楣蚕磉M(jìn)程數(shù)據(jù),數(shù)據(jù)共享簡單,但也是因?yàn)檫@個(gè)原因?qū)е峦綇?fù)雜 | 各有優(yōu)勢 |
| 內(nèi)存、CPU | 占用內(nèi)存多,切換復(fù)雜,CPU利用率低 | 占用內(nèi)存少,切換簡單,CPU利用率高 | 線程占優(yōu) |
| 創(chuàng)建銷毀、切換 | 創(chuàng)建銷毀、切換復(fù)雜,速度慢 | 創(chuàng)建銷毀、切換簡單,速度很快 | 線程占優(yōu) |
| 編程、調(diào)試 | 編程簡單,調(diào)試簡單 | 編程復(fù)雜,調(diào)試復(fù)雜 | 進(jìn)程占優(yōu) |
| 可靠性 | 進(jìn)程間不會(huì)互相影響 | 一個(gè)線程掛掉將導(dǎo)致整個(gè)進(jìn)程掛掉 | 進(jìn)程占優(yōu) |
| 分布式 | 適應(yīng)于多核、多機(jī)分布式;如果一臺(tái)機(jī)器不夠,擴(kuò)展到多臺(tái)機(jī)器比較簡單 | 適應(yīng)于多核分布式 | 進(jìn)程占優(yōu) ? |
1)需要頻繁創(chuàng)建銷毀的優(yōu)先用線程
原因請看上面的對比。
這種原則最常見的應(yīng)用就是Web服務(wù)器了,來一個(gè)連接建立一個(gè)線程,斷了就銷毀線程,要是用進(jìn)程,創(chuàng)建和銷毀的代價(jià)是很難承受的
2)需要進(jìn)行大量計(jì)算的優(yōu)先使用線程
所謂大量計(jì)算,當(dāng)然就是要耗費(fèi)很多CPU,切換頻繁了,這種情況下線程是最合適的。
這種原則最常見的是圖像處理、算法處理。
3)強(qiáng)相關(guān)的處理用線程,弱相關(guān)的處理用進(jìn)程
什么叫強(qiáng)相關(guān)、弱相關(guān)?理論上很難定義,給個(gè)簡單的例子就明白了。
一般的Server需要完成如下任務(wù):消息收發(fā)、消息處理。“消息收發(fā)”和“消息處理”就是弱相關(guān)的任務(wù),而“消息處理”里面可能又分為“消息解碼”、“業(yè)務(wù)處理”,這兩個(gè)任務(wù)相對來說相關(guān)性就要強(qiáng)多了。因此“消息收發(fā)”和“消息處理”可以分進(jìn)程設(shè)計(jì),“消息解碼”、“業(yè)務(wù)處理”可以分線程設(shè)計(jì)。
當(dāng)然這種劃分方式不是一成不變的,也可以根據(jù)實(shí)際情況進(jìn)行調(diào)整。
4)可能要擴(kuò)展到多機(jī)分布的用進(jìn)程,多核分布的用線程
原因請看上面對比。
5)都滿足需求的情況下,用你最熟悉、最拿手的方式
至于“數(shù)據(jù)共享、同步”、“編程、調(diào)試”、“可靠性”這幾個(gè)維度的所謂的“復(fù)雜、簡單”應(yīng)該怎么取舍,我只能說:沒有明確的選擇方法。但我可以告訴你一個(gè)選擇原則:如果多進(jìn)程和多線程都能夠滿足要求,那么選擇你最熟悉、最拿手的那個(gè)。?
需要提醒的是:雖然我給了這么多的選擇原則,但實(shí)際應(yīng)用中基本上都是“進(jìn)程+線程”的結(jié)合方式,千萬不要真的陷入一種非此即彼的誤區(qū)。
?
消耗資源:
從內(nèi)核的觀點(diǎn)看,進(jìn)程的目的就是擔(dān)當(dāng)分配系統(tǒng)資源(CPU時(shí)間、內(nèi)存等)的基本單位。線程是進(jìn)程的一個(gè)執(zhí)行流,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位。
線程,它們彼此之間使用相同的地址空間,共享大部分?jǐn)?shù)據(jù),啟動(dòng)一個(gè)線程所花費(fèi)的空間遠(yuǎn)遠(yuǎn)小于啟動(dòng)一個(gè)進(jìn)程所花費(fèi)的空間,而且,線程間彼此切換所需的時(shí)間也遠(yuǎn)遠(yuǎn)小于進(jìn)程間切換所需要的時(shí)間。據(jù)統(tǒng)計(jì),總的說來,一個(gè)進(jìn)程的開銷大約是一個(gè)線程開銷的30倍左右,當(dāng)然,在具體的系統(tǒng)上,這個(gè)數(shù)據(jù)可能會(huì)有較大的區(qū)別。
通訊方式:
進(jìn)程之間傳遞數(shù)據(jù)只能是通過通訊的方式,即費(fèi)時(shí)又不方便。線程時(shí)間數(shù)據(jù)大部分共享(線程函數(shù)內(nèi)部不共享),快捷方便。但是數(shù)據(jù)同步需要鎖對于static變量尤其注意
線程自身優(yōu)勢:
提高應(yīng)用程序響應(yīng);使多CPU系統(tǒng)更加有效。操作系統(tǒng)會(huì)保證當(dāng)線程數(shù)不大于CPU數(shù)目時(shí),不同的線程運(yùn)行于不同的CPU上;
改善程序結(jié)構(gòu)。一個(gè)既長又復(fù)雜的進(jìn)程可以考慮分為多個(gè)線程,成為幾個(gè)獨(dú)立或半獨(dú)立的運(yùn)行部分,這樣的程序會(huì)利于理解和修改。
?
1.?[代碼]PHP實(shí)現(xiàn)多進(jìn)程并行操作(可做守護(hù)進(jìn)程)?
1 /** 2 * 入口函數(shù) 3 * 將此文件保存為 ProcessOpera.php 4 * 在terminal中運(yùn)行 /usr/local/php/bin/php ProcessOpera.php & 5 * 查看進(jìn)程 ps aux|grep php 6 */ 7 8 9 ProcessOpera("runCode", array(), 8); 10 11 /** 12 * run Code 13 */ 14 function runCode($opt = array()) { 15 //需要在守護(hù)進(jìn)程中運(yùn)行的代碼 16 } 17 18 /** 19 * $func為子進(jìn)程執(zhí)行具體事物的函數(shù)名稱 20 * $opt為$func的參數(shù) 數(shù)組形式 21 * $pNum 為fork的子進(jìn)程數(shù)量 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實(shí)現(xiàn)多線程操作?
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 //派發(fā)任務(wù) 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!';來源:http://makaidong.com/binghuo000/0/1963_3326721.html
總結(jié)
以上是生活随笔為你收集整理的php实现多进程、多线程的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Redis队列php多线程请求
- 下一篇: sublime开启vim模式