- 相關(guān)推薦
php多進(jìn)程編程詳解2017
日常任務(wù)中,有時需要通過php腳本執(zhí)行一些日志分析,隊列處理等任務(wù),當(dāng)數(shù)據(jù)量比較大時,可以使用多進(jìn)程來處理。php多進(jìn)程是怎么寫的?下面yjbys小編為大家分享php多進(jìn)程編程,希望對大家有參考作用!
php單進(jìn)程存在的問題:
多核處理器未充分利用,而單處理器通常需要等待其他操作完成之后才能再繼續(xù)工作。 任何現(xiàn)代操作系統(tǒng)都可在幕后執(zhí)行多任務(wù),這意味著在很短時間內(nèi),計算機(jī)可以調(diào)度多個進(jìn)程,以執(zhí)行多個程序。
如果我們將所有的工作都局限在一個進(jìn)程中,它只能一次做一件事,這意味著我們需要將我們的單進(jìn)程任務(wù)變成一個多進(jìn)程任務(wù),以便我們可以利用 操作系統(tǒng)的多任務(wù)處理能力。
多進(jìn)程與多線程
在繼續(xù)之前,先解釋下多進(jìn)程和多線程之間的區(qū)別。
進(jìn)程,是具有其自己的存儲器空間,自己的進(jìn)程ID號等的程序的唯一實例。
線程,可以被認(rèn)為是一個虛擬進(jìn)程,它沒有自己的進(jìn)程ID,沒有自己的內(nèi)存空間,但仍然能夠利用多任務(wù)。
啟用超線程的CPU,通過動態(tài)生成線程,以盡可能避免延遲,從而進(jìn)一步推進(jìn)。
雖然有些人可能不同意,但大多數(shù)Unix程序員具有一定程度的不信任的線程。 Unix系統(tǒng)總是首選多進(jìn)程,然后才是多線程,部分原因是在Unix上創(chuàng)建一個進(jìn)程(通常稱為子進(jìn)程的“生成”或“分叉”)是非?斓。 在其他操作系統(tǒng)中,如Windows,fork相當(dāng)慢,所以線程概念更受歡迎。
考慮到這一點,毫不奇怪,所以目前只有在unix系統(tǒng)中支持php以fork多個進(jìn)程,這個擴(kuò)展是pcntl_fork函數(shù)
php如何進(jìn)行多進(jìn)程編程
在php中使用pcntl_fork擴(kuò)展函數(shù)進(jìn)行frok多個進(jìn)程。
pcntl_fork返回值說明
當(dāng)pcntl_fork函數(shù)被調(diào)用時,它將返回3個值。
如果返回值為-1,則fork失敗,并且沒有子進(jìn)程。 這可能是由于缺少內(nèi)存,或者因為已經(jīng)達(dá)到對用戶進(jìn)程數(shù)量的系統(tǒng)限制。
如果返回值是大于0的任何數(shù)字,當(dāng)前腳本是調(diào)用pcntl_fork()的父級,返回值是分叉的子進(jìn)程的進(jìn)程ID(PID)。 最后,如果返回值為0,則當(dāng)前腳本是被分叉的子節(jié)點。
pcntl_fork執(zhí)行原理
如果你成功的執(zhí)行pcntl_fork()函數(shù),將有兩個PHP副本同時執(zhí)行相同的腳本。 它們都從pcntl_fork()行繼續(xù)執(zhí)行,最重要的是,子進(jìn)程獲取父進(jìn)程中設(shè)置的所有變量的副本,甚至是資源。 我們忘記的一個關(guān)鍵的事情是,資源的副本不是一個獨立的資源,他們將指向同一個事情,這可能是有問題的,更多的詳情,稍后將繼續(xù)討論。
現(xiàn)在,這里有一個基本使用pcntl_fork()的例子:
<?php
$pid = pcntl_fork(); switch($pid) { case -1: print "Could not fork!\n"; exit; case 0: print "In child!\n"; break; default: print "In parent!\n";
}?>
上面的腳本只是在父進(jìn)程和子進(jìn)程中打印一條消息。 但是,它不顯示父項的變量數(shù)據(jù)如何被復(fù)制到子項,它輸出了2條信息,如下所示,說明已經(jīng)是有2個進(jìn)程在執(zhí)行了(其中一個是主進(jìn)程,一個是fork出來的子進(jìn)程)
[root@25f0b49dc696 wwwroot]# php fork.php In parent!In child!
接著看下面的例子:
<?php
$pid1 = pcntl_fork(); //第一次fork
$pid2 = pcntl_fork(); //第二次fork
$pid3 = pcntl_fork(); //第三次fork
$current_process_id = posix_getpid();
echo "current_process_id===$current_process_id===pid1==$pid1===pid2===$pid2==pid3==$pid3\n";
上面的例子,輸出結(jié)果如下:
current_process_id===13090===pid1==13091===pid2===13092==pid3==13093current_process_id===13093===pid1==13091===pid2===13092==pid3==0current_process_id===13092===pid1==13091===pid2===0==pid3==13094current_process_id===13094===pid1==13091===pid2===0==pid3==0current_process_id===13091===pid1==0===pid2===13095==pid3==13096current_process_id===13096===pid1==0===pid2===13095==pid3==0current_process_id===13095===pid1==0===pid2===0==pid3==13097current_process_id===13097===pid1==0===pid2===0==pid3==0
分析上面的結(jié)果,
可以看出,主進(jìn)程ID是13090
第一次fork
主13090 ->13091
第二次fork
主13090 ->13092
子13091 ->13095
第三次fork
主13090 ->13093
子13091 ->13096
子13092 ->13094
子13095 ->13097
至此,一共有8個進(jìn)程在執(zhí)行當(dāng)前腳本
接著看下面的例子:
<?php
$main_process_id = posix_getpid();
echo "the main process id==$main_process_id\n"; for ($i = 1; $i <= 5; ++$i) {
$pid = pcntl_fork();
$current_process_id = posix_getpid(); if (!$pid) {
echo "child $i current process id==$current_process_id==pid==$pid\n"; sleep(1); //sleep($i) print "In child $i\n"; //這里設(shè)置sleep不會阻塞輸出,1s后會自動結(jié)束進(jìn)程
//sleep(1); //結(jié)束當(dāng)前子進(jìn)程,不讓子進(jìn)程繼續(xù)fork,不會阻止父進(jìn)程繼續(xù)fork
exit;
} else{
echo "parent current process id==$current_process_id==pid==$pid\n"; print "In parent $i\n"; //fork完畢,退出父進(jìn)程,不讓下次參與fork,能保證執(zhí)行順序,但下一次的fork要等待子進(jìn)程執(zhí)行完成后才能fork
//exit;
}
}
這次五個子進(jìn)程被fork創(chuàng)建成功,并且,因為每個子進(jìn)程在父進(jìn)程最后設(shè)置的時候獲取$ i變量的副本,腳本打印出"In child 1", "In child 2", "In child 3", "In child 4", and "In child 5".
[root@25f0b49dc696 wwwroot]# php fork2.php the main process id==13163parent current process id==13163==pid==13164In parent 1parent current process id==13163==pid==13165In parent 2parent current process id==13163==pid==13166In parent 3parent current process id==13163==pid==13167In parent 4parent current process id==13163==pid==13168In parent 5child 3 current process id==13166==pid==0child 2 current process id==13165==pid==0child 4 current process id==13167==pid==0child 5 current process id==13168==pid==0child 1 current process id==13164==pid==0[root@25f0b49dc696 wwwroot]# In child 3In child 4In child 5In child 2In child 1
然而,一切都不是那么簡單,因為有兩個關(guān)鍵的事情要注意,當(dāng)你運行上述腳本。
首先,注意每個子腳本在打印出它的消息后調(diào)用exit。 在正常情況下,這將立即退出腳本,但在這里,它退出的是子PHP腳本,而不是父或任何其他子腳本。因此,每個其他子腳本和父腳本可以并且確實在一個孩子終止后繼續(xù)執(zhí)行。
其次,當(dāng)腳本運行時,它的輸出可能很混亂。
注意孩子們?nèi)绾伟错樞虼蛴〕鏊麄兊男畔ⅰ?雖然這可能是很常見的情況,你不能依靠你的孩子被執(zhí)行在一個特定的順序。
這是多處理器的基本原則之一:一旦產(chǎn)生了進(jìn)程,它就是操作系統(tǒng)決定何時執(zhí)行它以及給出多少時間。
還要注意我如何立即返回到我的shell提示,然后調(diào)用五個孩子打印出他們的消息,盡管我顯然已經(jīng)有控制權(quán)。
這樣做的原因是因為雖然孩子們附著在終端上,但他們基本上是在后臺運行的。 一旦父終止,命令提示符將重新出現(xiàn),你可以開始執(zhí)行其他程序,但是,正如你可以看到,孩子們?nèi)匀粫钴S,當(dāng)他們想(因為孩子們不會做)。 在沒有sleep命令情況下,這將不那么明顯,但是重要的是記住子進(jìn)程本質(zhì)上有自己的運行環(huán)境。
PHP,像任何父母,可以使其監(jiān)視其孩子,以確保他們做正確的事情。 這是通過兩個新函數(shù)來實現(xiàn)的:
pcntl_waitpid(),它指示PHP等待子進(jìn)程,
pcntl_wexitstatus(),它獲取一個終止子進(jìn)程返回的值。 我們已經(jīng)看過exit()函數(shù),以及如何使用它來向系統(tǒng)返回一個值
我們將使用這個值將值發(fā)送回父進(jìn)程,然后檢索使用pcntl_wexitstatus()。
在深入了解代碼之前,讓我先解釋一下這些新函數(shù)是如何使用的。
pcntl_waitpid
int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )
默認(rèn)情況下,pcntl_waitpid()將導(dǎo)致父進(jìn)程無限期地暫停,等待子進(jìn)程終止。
如果pid指定的子進(jìn)程在此函數(shù)調(diào)用時已經(jīng)退出(俗稱僵尸進(jìn)程),此函數(shù) 將立刻返回
至少需要兩個參數(shù),$pid-父類應(yīng)該等待的子進(jìn)程ID,$status-用來填充子進(jìn)程狀態(tài)的變量
$pid的值可以是以下之一:
< -1 等待任意進(jìn)程組ID等于參數(shù)pid給定值的絕對值的進(jìn)程。例如,如果傳遞-1802,pcntl_waitpid將等待進(jìn)程組ID為1802的任何子進(jìn)程。-1 等待任意子進(jìn)程;與pcntl_wait函數(shù)行為一致。0 等待任意與調(diào)用進(jìn)程組ID相同的子進(jìn)程。這是最常用的值。
> 0 等待進(jìn)程號等于參數(shù)pid值的子進(jìn)程。也就是說,如果你傳入1802,pcntl_waitpid將等待子進(jìn)程1802終止。
$status
pcntl_waitpid()將會存儲狀態(tài)信息到status 參數(shù)上,這個通過status參數(shù)返回的狀態(tài)信息可以用以下函數(shù) pcntl_wifexited(), pcntl_wifstopped(), pcntl_wifsignaled(), pcntl_wexitstatus(), pcntl_wtermsig()以及 pcntl_wstopsig()獲取其具體的值。
返回值
pcntl_waitpid()返回退出的子進(jìn)程進(jìn)程號,發(fā)生錯誤時返回-1
返回終止子進(jìn)程的PID,然后用狀態(tài)變量填充子進(jìn)程退出的信息。
如果調(diào)用pcntl_waitpid并且沒有子運行,則立即返回-1并且不填充狀態(tài)變量。
因此,如果0作為第一個參數(shù)傳遞給函數(shù),pcntl_waitpid()將等待它的任何子進(jìn)程終止。 當(dāng)它成立時,它返回子進(jìn)程的PID,終止并填充第二個參數(shù),并提供有關(guān)終止的子進(jìn)程的信息。 因為我們有幾個孩子,我們需要繼續(xù)調(diào)用pcntl_waitpid(),直到它返回-1,每次返回一些東西,我們應(yīng)該打印出來的子進(jìn)程的返回值。
從我們的子進(jìn)程返回一個值就像向exit()傳遞一個參數(shù)一樣簡單,而不僅僅是終止。 這通過pcntl_waitpid()的返回值返回父節(jié)點,返回一個狀態(tài)代碼。 此狀態(tài)代碼不直接求值為返回值,因為它包含兩個位的信息:子節(jié)點如何終止,以及如果子節(jié)點終止,則返回它的退出代碼。
現(xiàn)在我們只假設(shè)子節(jié)點自己終止,這意味著退出代碼總是設(shè)置在pcntl_waitpid()的返回值里面。 要從返回值提取退出代碼,使用pcntl_wexitstatus()函數(shù),它將返回值作為其唯一參數(shù),并返回子進(jìn)程的退出代碼。
這可能聽起來很復(fù)雜,但是一旦查看下一個代碼項目,它應(yīng)該會變得清楚。 這個例子顯示了我們討論的一切:
<?php for ($i = 1; $i <= 5; ++$i) {
$pid = pcntl_fork(); if (!$pid) { sleep(1);
$current_process_id = posix_getpid(); print "In child $i===process_id===$current_process_id\n"; exit($i);
}
} while (($pid = pcntl_waitpid(0, $status)) != -1) {
$status = pcntl_wexitstatus($status);
echo "Child $status completed==pid==$pid\n";
}
?>
上例將輸出,同時也驗證了pcntl_waitpid返回的pid是正確的
In child 1===process_id===13106In child 5===process_id===13110In child 4===process_id===13109In child 3===process_id===13108In child 2===process_id===13107Child 4 completed==pid==13109Child 5 completed==pid==13110Child 1 completed==pid==13106Child 3 completed==pid==13108Child 2 completed==pid==13107
注意,通過使用exit($ i);每個子節(jié)點返回它在屏幕上打印出來的數(shù)字作為其退出代碼。 主while循環(huán)再次調(diào)用pcntl_waitpid(),直到它返回-1(沒有子節(jié)點),并且對于每個終止的子節(jié)點,它使用pcntl_wexitstatus()提取出口代碼并打印出來。 注意,pcntl_waitpid()的第一個參數(shù)是0,這意味著它將等待所有的孩子。
運行該腳本應(yīng)該停止命令提示符,直到所有五個孩子終止,這是理想的。
【php多進(jìn)程編程詳解】相關(guān)文章:
PHP Socket編程過程02-09
php實習(xí)心得12-01
php實習(xí)報告11-07
php工作總結(jié)11-11
php是什么格式?01-14
php開發(fā)主管的職責(zé)05-15
數(shù)控編程代碼大全02-13
php開發(fā)主管的工作職責(zé)09-29
php實習(xí)報告(5篇)11-08
php實習(xí)心得7篇12-02