當前位置:才華齋>設計>網頁設計>

php多程序程式設計詳解2017

網頁設計 閱讀(2.8W)

日常任務中,有時需要通過php指令碼執行一些日誌分析,佇列處理等任務,當資料量比較大時,可以使用多程序來處理。php多程序是怎麼寫的?下面yjbys小編為大家分享php多程序程式設計,希望對大家有參考作用!

php多程序程式設計詳解2017

  php單程序存在的問題:

多核處理器未充分利用,而單處理器通常需要等待其他操作完成之後才能再繼續工作。 任何現代作業系統都可在幕後執行多工,這意味著在很短時間內,計算機可以排程多個程序,以執行多個程式。

如果我們將所有的工作都侷限在一個程序中,它只能一次做一件事,這意味著我們需要將我們的單程序任務變成一個多程序任務,以便我們可以利用 作業系統的多工處理能力。

  多程序與多執行緒

在繼續之前,先解釋下多程序和多執行緒之間的區別。

程序,是具有其自己的儲存器空間,自己的程序ID號等的程式的唯一例項。

執行緒,可以被認為是一個虛擬程序,它沒有自己的程序ID,沒有自己的記憶體空間,但仍然能夠利用多工。

啟用超執行緒的CPU,通過動態生成執行緒,以儘可能避免延遲,從而進一步推進。

雖然有些人可能不同意,但大多數Unix程式設計師具有一定程度的不信任的執行緒。 Unix系統總是首選多程序,然後才是多執行緒,部分原因是在Unix上建立一個程序(通常稱為子程序的“生成”或“分叉”)是非常快的。 在其他作業系統中,如Windows,fork相當慢,所以執行緒概念更受歡迎。

考慮到這一點,毫不奇怪,所以目前只有在unix系統中支援php以fork多個程序,這個擴充套件是pcntl_fork函式

  php如何進行多程序程式設計

在php中使用pcntl_fork擴充套件函式進行frok多個程序。

  pcntl_fork返回值說明

當pcntl_fork函式被呼叫時,它將返回3個值。

如果返回值為-1,則fork失敗,並且沒有子程序。 這可能是由於缺少記憶體,或者因為已經達到對使用者程序數量的系統限制。

如果返回值是大於0的任何數字,當前指令碼是呼叫pcntl_fork()的父級,返回值是分叉的子程序的程序ID(PID)。 最後,如果返回值為0,則當前指令碼是被分叉的子節點。

  pcntl_fork執行原理

如果你成功的執行pcntl_fork()函式,將有兩個PHP副本同時執行相同的指令碼。 它們都從pcntl_fork()行繼續執行,最重要的是,子程序獲取父程序中設定的所有變數的副本,甚至是資源。 我們忘記的一個關鍵的事情是,資源的副本不是一個獨立的資源,他們將指向同一個事情,這可能是有問題的,更多的詳情,稍後將繼續討論。

現在,這裡有一個基本使用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";

}?>

上面的指令碼只是在父程序和子程序中列印一條訊息。 但是,它不顯示父項的變數資料如何被複制到子項,它輸出了2條資訊,如下所示,說明已經是有2個程序在執行了(其中一個是主程序,一個是fork出來的子程序)

[root@25f0b49dc696 wwwroot]# 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==$pid3n";

上面的例子,輸出結果如下:

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

分析上面的結果,

可以看出,主程序ID是13090

第一次fork

主13090 ->13091

第二次fork

主13090 ->13092

子13091 ->13095

第三次fork

主13090 ->13093

子13091 ->13096

子13092 ->13094

子13095 ->13097

至此,一共有8個程序在執行當前指令碼

接著看下面的例子:

<?php

$main_process_id = posix_getpid();

echo "the main process id==$main_process_idn"; 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==$pidn"; sleep(1); //sleep($i) print "In child $in"; //這裡設定sleep不會阻塞輸出,1s後會自動結束程序

//sleep(1); //結束當前子程序,不讓子程序繼續fork,不會阻止父程序繼續fork

exit;

} else{

echo "parent current process id==$current_process_id==pid==$pidn"; print "In parent $in"; //fork完畢,退出父程序,不讓下次參與fork,能保證執行順序,但下一次的fork要等待子程序執行完成後才能fork

//exit;

}

}

這次五個子程序被fork建立成功,並且,因為每個子程序在父程序最後設定的時候獲取$ i變數的副本,指令碼打印出"In child 1", "In child 2", "In child 3", "In child 4", and "In child 5".

[root@25f0b49dc696 wwwroot]# 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

然而,一切都不是那麼簡單,因為有兩個關鍵的事情要注意,當你執行上述指令碼。

首先,注意每個子指令碼在打印出它的訊息後呼叫exit。 在正常情況下,這將立即退出指令碼,但在這裡,它退出的是子PHP指令碼,而不是父或任何其他子指令碼。因此,每個其他子指令碼和父指令碼可以並且確實在一個孩子終止後繼續執行。

其次,當指令碼執行時,它的輸出可能很混亂。

注意孩子們如何按順序打印出他們的資訊。 雖然這可能是很常見的情況,你不能依靠你的孩子被執行在一個特定的順序。

這是多處理器的基本原則之一:一旦產生了程序,它就是作業系統決定何時執行它以及給出多少時間。

還要注意我如何立即返回到我的shell提示,然後呼叫五個孩子打印出他們的訊息,儘管我顯然已經有控制權。

這樣做的原因是因為雖然孩子們附著在終端上,但他們基本上是在後臺執行的。 一旦父終止,命令提示符將重新出現,你可以開始執行其他程式,但是,正如你可以看到,孩子們仍然會活躍,當他們想(因為孩子們不會做)。 在沒有sleep命令情況下,這將不那麼明顯,但是重要的是記住子程序本質上有自己的執行環境

PHP,像任何父母,可以使其監視其孩子,以確保他們做正確的事情。 這是通過兩個新函式來實現的:

pcntl_waitpid(),它指示PHP等待子程序,

pcntl_wexitstatus(),它獲取一個終止子程序返回的值。 我們已經看過exit()函式,以及如何使用它來向系統返回一個值

我們將使用這個值將值傳送回父程序,然後檢索使用pcntl_wexitstatus()。

在深入瞭解程式碼之前,讓我先解釋一下這些新函式是如何使用的。

pcntl_waitpid

int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )

預設情況下,pcntl_waitpid()將導致父程序無限期地暫停,等待子程序終止。

如果pid指定的子程序在此函式呼叫時已經退出(俗稱殭屍程序),此函式 將立刻返回

至少需要兩個引數,$pid-父類應該等待的子程序ID,$status-用來填充子程序狀態的變數

$pid的值可以是以下之一:

< -1 等待任意程序組ID等於引數pid給定值的絕對值的.程序。例如,如果傳遞-1802,pcntl_waitpid將等待程序組ID為1802的任何子程序。-1 等待任意子程序;與pcntl_wait函式行為一致。0 等待任意與呼叫程序組ID相同的子程序。這是最常用的值。

> 0 等待程序號等於引數pid值的子程序。也就是說,如果你傳入1802,pcntl_waitpid將等待子程序1802終止。

$status

pcntl_waitpid()將會儲存狀態資訊到status 引數上,這個通過status引數返回的狀態資訊可以用以下函式 pcntl_wifexited(), pcntl_wifstopped(), pcntl_wifsignaled(), pcntl_wexitstatus(), pcntl_wtermsig()以及 pcntl_wstopsig()獲取其具體的值。

返回值

pcntl_waitpid()返回退出的子程序程序號,發生錯誤時返回-1

返回終止子程序的PID,然後用狀態變數填充子程序退出的資訊。

如果呼叫pcntl_waitpid並且沒有子執行,則立即返回-1並且不填充狀態變數。

因此,如果0作為第一個引數傳遞給函式,pcntl_waitpid()將等待它的任何子程序終止。 當它成立時,它返回子程序的PID,終止並填充第二個引數,並提供有關終止的子程序的資訊。 因為我們有幾個孩子,我們需要繼續呼叫pcntl_waitpid(),直到它返回-1,每次返回一些東西,我們應該打印出來的子程序的返回值。

從我們的子程序返回一個值就像向exit()傳遞一個引數一樣簡單,而不僅僅是終止。 這通過pcntl_waitpid()的返回值返回父節點,返回一個狀態程式碼。 此狀態程式碼不直接求值為返回值,因為它包含兩個位的資訊:子節點如何終止,以及如果子節點終止,則返回它的退出程式碼。

現在我們只假設子節點自己終止,這意味著退出程式碼總是設定在pcntl_waitpid()的返回值裡面。 要從返回值提取退出程式碼,使用pcntl_wexitstatus()函式,它將返回值作為其唯一引數,並返回子程序的退出程式碼。

這可能聽起來很複雜,但是一旦檢視下一個程式碼專案,它應該會變得清楚。 這個例子顯示了我們討論的一切:

<?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_idn"; exit($i);

}

} while (($pid = pcntl_waitpid(0, $status)) != -1) {

$status = pcntl_wexitstatus($status);

echo "Child $status completed==pid==$pidn";

}

?>

上例將輸出,同時也驗證了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);每個子節點返回它在螢幕上打印出來的數字作為其退出程式碼。 主while迴圈再次呼叫pcntl_waitpid(),直到它返回-1(沒有子節點),並且對於每個終止的子節點,它使用pcntl_wexitstatus()提取出口程式碼並打印出來。 注意,pcntl_waitpid()的第一個引數是0,這意味著它將等待所有的孩子。

執行該指令碼應該停止命令提示符,直到所有五個孩子終止,這是理想的。