以實(shí)例全面講解PHP中多進(jìn)程編程的相關(guān)函數(shù)的使用
PHP有一組進(jìn)程控制函數(shù)(編譯時(shí)需要–enable-pcntl與posix擴(kuò)展),使得php能實(shí)現(xiàn)跟c一樣的創(chuàng)建子進(jìn)程、使用exec函數(shù)執(zhí)行程序、處理信號等功能。
<?php header('content-type:text/html;charset=utf-8' ); // 必須加載擴(kuò)展 if (!function_exists("pcntl_fork")) { die("pcntl extention is must !"); } //總進(jìn)程的數(shù)量 $totals = 3; // 執(zhí)行的腳本數(shù)量 $cmdArr = array(); // 執(zhí)行的腳本數(shù)量的數(shù)組 for ($i = 0; $i < $totals; $i++) { $cmdArr[] = array("path" => __DIR__ . "/run.php", 'pid' =>$i ,'total' =>$totals); } /* 展開:$cmdArr Array ( [0] => Array ( [path] => /var/www/html/company/pcntl/run.php [pid] => 0 [total] => 3 ) [1] => Array ( [path] => /var/www/html/company/pcntl/run.php [pid] => 1 [total] => 3 ) [2] => Array ( [path] => /var/www/html/company/pcntl/run.php [pid] => 2 [total] => 3 ) ) */ pcntl_signal(SIGCHLD, SIG_IGN); //如果父進(jìn)程不關(guān)心子進(jìn)程什么時(shí)候結(jié)束,子進(jìn)程結(jié)束后,內(nèi)核會回收。 foreach ($cmdArr as $cmd) { $pid = pcntl_fork(); //創(chuàng)建子進(jìn)程 //父進(jìn)程和子進(jìn)程都會執(zhí)行下面代碼 if ($pid == -1) { //錯誤處理:創(chuàng)建子進(jìn)程失敗時(shí)返回-1. die('could not fork'); } else if ($pid) { //父進(jìn)程會得到子進(jìn)程號,所以這里是父進(jìn)程執(zhí)行的邏輯 //如果不需要阻塞進(jìn)程,而又想得到子進(jìn)程的退出狀態(tài),則可以注釋掉pcntl_wait($status)語句,或?qū)懗桑? pcntl_wait($status,WNOHANG); //等待子進(jìn)程中斷,防止子進(jìn)程成為僵尸進(jìn)程。 } else { //子進(jìn)程得到的$pid為0, 所以這里是子進(jìn)程執(zhí)行的邏輯。 $path = $cmd["path"]; $pid = $cmd['pid'] ; $total = $cmd['total'] ; echo exec("/usr/bin/php {$path} {$pid} {$total}")."\n"; exit(0) ; } } ?>
使用PHP真正的多進(jìn)程運(yùn)行模式,適用于數(shù)據(jù)采集、郵件群發(fā)、數(shù)據(jù)源更新、tcp服務(wù)器等環(huán)節(jié)。
PHP有一組進(jìn)程控制函數(shù)(編譯時(shí)需要 –enable-pcntl與posix擴(kuò)展),使得php能在*nix系統(tǒng)中實(shí)現(xiàn)跟c一樣的創(chuàng)建子進(jìn)程、使用exec函數(shù)執(zhí)行程序、處理信號等功能。 PCNTL使用ticks來作為信號處理機(jī)制(signal handle callback mechanism),可以最小程度地降低處理異步事件時(shí)的負(fù)載。何謂ticks?Tick 是一個(gè)在代碼段中解釋器每執(zhí)行 N 條低級語句就會發(fā)生的事件,這個(gè)代碼段需要通過declare來指定。
常用的PCNTL函數(shù)
1. pcntl_alarm ( int $seconds )
設(shè)置一個(gè)$seconds秒后發(fā)送SIGALRM信號的計(jì)數(shù)器
2. pcntl_signal ( int $signo , callback $handler [, bool $restart_syscalls ] )
為$signo設(shè)置一個(gè)處理該信號的回調(diào)函數(shù)。下面是一個(gè)隔5秒發(fā)送一個(gè)SIGALRM信號,并由signal_handler函數(shù)獲取,然后打印一個(gè)“Caught SIGALRM”的例子:
<?php declare(ticks = 1); function signal_handler($signal) { print "Caught SIGALRM\n"; pcntl_alarm(5); } pcntl_signal(SIGALRM, "signal_handler", true); pcntl_alarm(5); for(;;) { } ?>
3. pcntl_exec ( string $path [, array $args [, array $envs ]] )
在當(dāng)前的進(jìn)程空間中執(zhí)行指定程序,類似于c中的exec族函數(shù)。所謂當(dāng)前空間,即載入指定程序的代碼覆蓋掉當(dāng)前進(jìn)程的空間,執(zhí)行完該程序進(jìn)程即結(jié)束。
<?php $dir = '/home/shankka/'; $cmd = 'ls'; $option = '-l'; $pathtobin = '/bin/ls'; $arg = array($cmd, $option, $dir); pcntl_exec($pathtobin, $arg); echo '123'; //不會執(zhí)行到該行 ?>
4. pcntl_fork ( void )
為當(dāng)前進(jìn)程創(chuàng)建一個(gè)子進(jìn)程,并且先運(yùn)行父進(jìn)程,返回的是子進(jìn)程的PID,肯定大于零。在父進(jìn)程的代碼中可以用 pcntl_wait(&$status)暫停父進(jìn)程知道他的子進(jìn)程有返回值。注意:父進(jìn)程的阻塞同時(shí)會阻塞子進(jìn)程。但是父進(jìn)程的結(jié)束不影響子進(jìn)程的運(yùn)行。
父進(jìn)程運(yùn)行完了會接著運(yùn)行子進(jìn)程,這時(shí)子進(jìn)程會從執(zhí)行pcntl_fork()的那條語句開始執(zhí)行(包括此函數(shù)),但是此時(shí)它返回的是零(代表這是一個(gè)子進(jìn)程)。在子進(jìn)程的代碼塊中最好有exit語句,即執(zhí)行完子進(jìn)程后立即就結(jié)束。否則它會又重頭開始執(zhí)行這個(gè)腳本的某些部分。
注意兩點(diǎn):
- 子進(jìn)程最好有一個(gè)exit;語句,防止不必要的出錯;
- pcntl_fork間最好不要有其它語句,例如:
<?php $pid = pcntl_fork(); //這里最好不要有其他的語句 if ($pid == -1) { die('could not fork'); } else if ($pid) { // we are the parent pcntl_wait($status); //Protect against Zombie children } else { // we are the child } ?>
5. pcntl_wait ( int &$status [, int $options ] )
阻塞當(dāng)前進(jìn)程,只到當(dāng)前進(jìn)程的一個(gè)子進(jìn)程退出或者收到一個(gè)結(jié)束當(dāng)前進(jìn)程的信號。使用$status返回子進(jìn)程的狀態(tài)碼,并可以指定第二個(gè)參數(shù)來說明是否以阻塞狀態(tài)調(diào)用:
阻塞方式調(diào)用的,函數(shù)返回值為子進(jìn)程的pid,如果沒有子進(jìn)程返回值為-1;
非阻塞方式調(diào)用,函數(shù)還可以在有子進(jìn)程在運(yùn)行但沒有結(jié)束的子進(jìn)程時(shí)返回0。
6. pcntl_waitpid ( int $pid , int &$status [, int $options ] )
功能同pcntl_wait,區(qū)別為waitpid為等待指定pid的子進(jìn)程。當(dāng)pid為-1時(shí)pcntl_waitpid與pcntl_wait 一樣。在pcntl_wait和pcntl_waitpid兩個(gè)函數(shù)中的$status中存了子進(jìn)程的狀態(tài)信息,這個(gè)參數(shù)可以用于 pcntl_wifexited、pcntl_wifstopped、pcntl_wifsignaled、pcntl_wexitstatus、 pcntl_wtermsig、pcntl_wstopsig、pcntl_waitpid這些函數(shù)。
例如:
<?php $pid = pcntl_fork(); if($pid) { pcntl_wait($status); $id = getmypid(); echo "parent process,pid {$id}, child pid {$pid}\n"; }else{ $id = getmypid(); echo "child process,pid {$id}\n"; sleep(2); } ?>
子進(jìn)程在輸出child process等字樣之后sleep了2秒才結(jié)束,而父進(jìn)程阻塞著直到子進(jìn)程退出之后才繼續(xù)運(yùn)行。
7. pcntl_getpriority ([ int $pid [, int $process_identifier ]] )
取得進(jìn)程的優(yōu)先級,即nice值,默認(rèn)為0,在我的測試環(huán)境的linux中(CentOS release 5.2 (Final)),優(yōu)先級為-20到19,-20為優(yōu)先級最高,19為最低。(手冊中為-20到20)。
8. pcntl_setpriority ( int $priority [, int $pid [, int $process_identifier ]] )
設(shè)置進(jìn)程的優(yōu)先級。
9. posix_kill
可以給進(jìn)程發(fā)送信號
10. pcntl_singal
用來設(shè)置信號的回調(diào)函數(shù)
當(dāng)父進(jìn)程退出時(shí),子進(jìn)程如何得知父進(jìn)程的退出
當(dāng)父進(jìn)程退出時(shí),子進(jìn)程一般可以通過下面這兩個(gè)比較簡單的方法得知父進(jìn)程已經(jīng)退出這個(gè)消息:
當(dāng)父進(jìn)程退出時(shí),會有一個(gè)INIT進(jìn)程來領(lǐng)養(yǎng)這個(gè)子進(jìn)程。這個(gè)INIT進(jìn)程的進(jìn)程號為1,所以子進(jìn)程可以通過使用getppid()來取得當(dāng)前父進(jìn)程的pid。如果返回的是1,表明父進(jìn)程已經(jīng)變?yōu)镮NIT進(jìn)程,則原進(jìn)程已經(jīng)推出。
使用kill函數(shù),向原有的父進(jìn)程發(fā)送空信號(kill(pid, 0))。使用這個(gè)方法對某個(gè)進(jìn)程的存在性進(jìn)行檢查,而不會真的發(fā)送信號。所以,如果這個(gè)函數(shù)返回-1表示父進(jìn)程已經(jīng)退出。
除了上面的這兩個(gè)方法外,還有一些實(shí)現(xiàn)上比較復(fù)雜的方法,比如建立管道或socket來進(jìn)行時(shí)時(shí)的監(jiān)控等等。
PHP多進(jìn)程采集數(shù)據(jù)的例子
<?php /** * Project: Signfork: php多線程庫 * File: Signfork.class.php */ class Signfork{ /** * 設(shè)置子進(jìn)程通信文件所在目錄 * @var string */ private $tmp_path='/tmp/'; /** * Signfork引擎主啟動方法 * 1、判斷$arg類型,類型為數(shù)組時(shí)將值傳遞給每個(gè)子進(jìn)程;類型為數(shù)值型時(shí),代表要創(chuàng)建的進(jìn)程數(shù). * @param object $obj 執(zhí)行對象 * @param string|array $arg 用于對象中的__fork方法所執(zhí)行的參數(shù) * 如:$arg,自動分解為:$obj->__fork($arg[0])、$obj->__fork($arg[1])... * @return array 返回 array(子進(jìn)程序列=>子進(jìn)程執(zhí)行結(jié)果); */ public function run($obj,$arg=1){ if(!method_exists($obj,'__fork')){ exit("Method '__fork' not found!"); } if(is_array($arg)){ $i=0; foreach($arg as $key=>$val){ $spawns[$i]=$key; $i++; $this->spawn($obj,$key,$val); } $spawns['total']=$i; }elseif($spawns=intval($arg)){ for($i = 0; $i < $spawns; $i++){ $this->spawn($obj,$i); } }else{ exit('Bad argument!'); } if($i>1000) exit('Too many spawns!'); return $this->request($spawns); } /** * Signfork主進(jìn)程控制方法 * 1、$tmpfile 判斷子進(jìn)程文件是否存在,存在則子進(jìn)程執(zhí)行完畢,并讀取內(nèi)容 * 2、$data收集子進(jìn)程運(yùn)行結(jié)果及數(shù)據(jù),并用于最終返回 * 3、刪除子進(jìn)程文件 * 4、輪詢一次0.03秒,直到所有子進(jìn)程執(zhí)行完畢,清理子進(jìn)程資源 * @param string|array $arg 用于對應(yīng)每個(gè)子進(jìn)程的ID * @return array 返回 array([子進(jìn)程序列]=>[子進(jìn)程執(zhí)行結(jié)果]); */ private function request($spawns){ $data=array(); $i=is_array($spawns)?$spawns['total']:$spawns; for($ids = 0; $ids<$i; $ids++){ while(!($cid=pcntl_waitpid(-1, $status, WNOHANG)))usleep(30000); $tmpfile=$this->tmp_path.'sfpid_'.$cid; $data[$spawns['total']?$spawns[$ids]:$ids]=file_get_contents($tmpfile); unlink($tmpfile); } return $data; } /** * Signfork子進(jìn)程執(zhí)行方法 * 1、pcntl_fork 生成子進(jìn)程 * 2、file_put_contents 將'$obj->__fork($val)'的執(zhí)行結(jié)果存入特定序列命名的文本 * 3、posix_kill殺死當(dāng)前進(jìn)程 * @param object $obj 待執(zhí)行的對象 * @param object $i 子進(jìn)程的序列ID,以便于返回對應(yīng)每個(gè)子進(jìn)程數(shù)據(jù) * @param object $param 用于輸入對象$obj方法'__fork'執(zhí)行參數(shù) */ private function spawn($obj,$i,$param=null){ if(pcntl_fork()===0){ $cid=getmypid(); file_put_contents($this->tmp_path.'sfpid_'.$cid,$obj->__fork($param)); posix_kill($cid, SIGTERM); exit; } } } ?>
php在pcntl_fork()后生成的子進(jìn)程(通常為僵尸進(jìn)程)必須由pcntl_waitpid()函數(shù)進(jìn)行資源釋放。但在 pcntl_waitpid()不一定釋放的就是當(dāng)前運(yùn)行的進(jìn)程,也可能是過去生成的僵尸進(jìn)程(沒有釋放);也可能是并發(fā)時(shí)其它訪問者的僵尸進(jìn)程。但可以使用posix_kill($cid, SIGTERM)在子進(jìn)程結(jié)束時(shí)殺掉它。
子進(jìn)程會自動復(fù)制父進(jìn)程空間里的變量。
PHP多進(jìn)程編程示例2
<?php //..... //需要安裝pcntl的php擴(kuò)展,并加載它 if(function_exists("pcntl_fork")){ //生成子進(jìn)程 $pid = pcntl_fork(); if($pid == -1){ die('could not fork'); }else{ if($pid){ $status = 0; //阻塞父進(jìn)程,直到子進(jìn)程結(jié)束,不適合需要長時(shí)間運(yùn)行的腳本,可使用pcntl_wait($status, 0)實(shí)現(xiàn)非阻塞式 pcntl_wait($status); // parent proc code exit; }else{ // child proc code //結(jié)束當(dāng)前子進(jìn)程,以防止生成僵尸進(jìn)程 if(function_exists("posix_kill")){ posix_kill(getmypid(), SIGTERM); }else{ system('kill -9'. getmypid()); } exit; } } }else{ // 不支持多進(jìn)程處理時(shí)的代碼在這里 } //..... ?> 如果不需要阻塞進(jìn)程,而又想得到子進(jìn)程的退出狀態(tài),則可以注釋掉pcntl_wait($status)語句,或?qū)懗桑? <?php pcntl_wait($status, 1); //或 pcntl_wait($status, WNOHANG); ?>
在上面的代碼中,如果父進(jìn)程退出(使用exit函數(shù)退出或redirect),則會導(dǎo)致子進(jìn)程成為僵尸進(jìn)程(會交給init進(jìn)程控制),子進(jìn)程不再執(zhí)行。
僵尸進(jìn)程是指的父進(jìn)程已經(jīng)退出,而該進(jìn)程dead之后沒有進(jìn)程接受,就成為僵尸進(jìn)程.(zombie)進(jìn)程。任何進(jìn)程在退出前(使用exit退出) 都會變成僵尸進(jìn)程(用于保存進(jìn)程的狀態(tài)等信息),然后由init進(jìn)程接管。如果不及時(shí)回收僵尸進(jìn)程,那么它在系統(tǒng)中就會占用一個(gè)進(jìn)程表項(xiàng),如果這種僵尸進(jìn)程過多,最后系統(tǒng)就沒有可以用的進(jìn)程表項(xiàng),于是也無法再運(yùn)行其它的程序。
預(yù)防僵尸進(jìn)程有以下幾種方法:
1. 父進(jìn)程通過wait和waitpid等函數(shù)使其等待子進(jìn)程結(jié)束,然后再執(zhí)行父進(jìn)程中的代碼,這會導(dǎo)致父進(jìn)程掛起。上面的代碼就是使用這種方式實(shí)現(xiàn)的,但在WEB環(huán)境下,它不適合子進(jìn)程需要長時(shí)間運(yùn)行的情況(會導(dǎo)致超時(shí))。
使用wait和waitpid方法使父進(jìn)程自動回收其僵尸子進(jìn)程(根據(jù)子進(jìn)程的返回狀態(tài)),waitpid用于臨控指定子進(jìn)程,wait是對于所有子進(jìn)程而言。
2. 如果父進(jìn)程很忙,那么可以用signal函數(shù)為SIGCHLD安裝handler,因?yàn)樽舆M(jìn)程結(jié)束后,父進(jìn)程會收到該信號,可以在handler中調(diào)用wait回收
3. 如果父進(jìn)程不關(guān)心子進(jìn)程什么時(shí)候結(jié)束,那么可以用signal(SIGCHLD, SIG_IGN)通知內(nèi)核,自己對子進(jìn)程的結(jié)束不感興趣,那么子進(jìn)程結(jié)束后,內(nèi)核會回收,并不再給父進(jìn)程發(fā)送信號,例如:
<?php pcntl_signal(SIGCHLD, SIG_IGN); $pid = pcntl_fork(); //....code ?>
4. 還有一個(gè)技巧,就是fork兩次,父進(jìn)程fork一個(gè)子進(jìn)程,然后繼續(xù)工作,子進(jìn)程再fork一個(gè)孫進(jìn)程后退出,那么孫進(jìn)程被init接管,孫進(jìn)程結(jié)束后,init會回收。不過子進(jìn)程的回收還要自己做。下面是一個(gè)例子:
#include "apue.h" #include <sys/wait.h> int main(void){ pid_t pid; if ((pid = fork()) < 0){ err_sys("fork error"); } else if (pid == 0){ /**//* first child */ if ((pid = fork()) < 0){ err_sys("fork error"); }elseif(pid > 0){ exit(0); /**//* parent from second fork == first child */ } /** * We're the second child; our parent becomes init as soon * as our real parent calls exit() in the statement above. * Here's where we'd continue executing, knowing that when * we're done, init will reap our status. */ sleep(2); printf("second child, parent pid = %d ", getppid()); exit(0); } if (waitpid(pid, NULL, 0) != pid) /**//* wait for first child */ err_sys("waitpid error"); /** * We're the parent (the original process); we continue executing, * knowing that we're not the parent of the second child. */ exit(0); }
在fork()/execve()過程中,假設(shè)子進(jìn)程結(jié)束時(shí)父進(jìn)程仍存在,而父進(jìn)程fork()之前既沒安裝SIGCHLD信號處理函數(shù)調(diào)用 waitpid()等待子進(jìn)程結(jié)束,又沒有顯式忽略該信號,則子進(jìn)程成為僵尸進(jìn)程,無法正常結(jié)束,此時(shí)即使是root身份kill-9也不能殺死僵尸進(jìn)程。補(bǔ)救辦法是殺死僵尸進(jìn)程的父進(jìn)程(僵尸進(jìn)程的父進(jìn)程必然存在),僵尸進(jìn)程成為”孤兒進(jìn)程”,過繼給1號進(jìn)程init,init會定期調(diào)用wait回收清理這些父進(jìn)程已退出的僵尸子進(jìn)程。
所以,上面的示例可以改成:
<?php //..... //需要安裝pcntl的php擴(kuò)展,并加載它 if(function_exists("pcntl_fork")){ //生成第一個(gè)子進(jìn)程 $pid = pcntl_fork(); //$pid即所產(chǎn)生的子進(jìn)程id if($pid == -1){ //子進(jìn)程fork失敗 die('could not fork'); }else{ if($pid){ //父進(jìn)程code sleep(5); //等待5秒 exit(0); //或$this->_redirect('/'); }else{ //第一個(gè)子進(jìn)程code //產(chǎn)生孫進(jìn)程 if(($gpid = pcntl_fork()) < 0){ ////$gpid即所產(chǎn)生的孫進(jìn)程id //孫進(jìn)程產(chǎn)生失敗 die('could not fork'); }elseif($gpid > 0){ //第一個(gè)子進(jìn)程code,即孫進(jìn)程的父進(jìn)程 $status = 0; $status = pcntl_wait($status); //阻塞子進(jìn)程,并返回孫進(jìn)程的退出狀態(tài),用于檢查是否正常退出 if($status ! = 0) file_put_content('filename', '孫進(jìn)程異常退出'); //得到父進(jìn)程id //$ppid = posix_getppid(); //如果$ppid為1則表示其父進(jìn)程已變?yōu)閕nit進(jìn)程,原父進(jìn)程已退出 //得到子進(jìn)程id:posix_getpid()或getmypid()或是fork返回的變量$pid //kill掉子進(jìn)程 //posix_kill(getmypid(), SIGTERM); exit(0); }else{ //即$gpid == 0 //孫進(jìn)程code //.... //結(jié)束孫進(jìn)程(即當(dāng)前進(jìn)程),以防止生成僵尸進(jìn)程 if(function_exists('posix_kill')){ posix_kill(getmypid(), SIGTERM); }else{ system('kill -9'. getmypid()); } exit(0); } } } }else{ // 不支持多進(jìn)程處理時(shí)的代碼在這里 } //..... ?>
怎樣產(chǎn)生僵尸進(jìn)程的
一個(gè)進(jìn)程在調(diào)用exit命令結(jié)束自己的生命的時(shí)候,其實(shí)它并沒有真正的被銷毀,而是留下一個(gè)稱為僵尸進(jìn)程(Zombie)的數(shù)據(jù)結(jié)構(gòu)(系統(tǒng)調(diào)用exit,它的作用是使進(jìn)程退出,但也僅僅限于將一個(gè)正常的進(jìn)程變成一個(gè)僵尸進(jìn)程,并不能將其完全銷毀)。在Linux進(jìn)程的狀態(tài)中,僵尸進(jìn)程是非常特殊的一種,它已經(jīng)放棄了幾乎所有內(nèi)存空間,沒有任何可執(zhí)行代碼,也不能被調(diào)度,僅僅在進(jìn)程列表中保留一個(gè)位置,記載該進(jìn)程的退出狀態(tài)等信息供其他進(jìn)程收集,除此之外,僵尸進(jìn)程不再占有任何內(nèi)存空間。它需要它的父進(jìn)程來為它收尸,如果他的父進(jìn)程沒安裝SIGCHLD信號處理函數(shù)調(diào)用wait或waitpid()等待子進(jìn)程結(jié)束,又沒有顯式忽略該信號,那么它就一直保持僵尸狀態(tài),如果這時(shí)父進(jìn)程結(jié)束了,那么init進(jìn)程自動會接手這個(gè)子進(jìn)程,為它收尸,它還是能被清除的。但是如果如果父進(jìn)程是一個(gè)循環(huán),不會結(jié)束,那么子進(jìn)程就會一直保持僵尸狀態(tài),這就是為什么系統(tǒng)中有時(shí)會有很多的僵尸進(jìn)程。
任何一個(gè)子進(jìn)程(init除外)在exit()之后,并非馬上就消失掉,而是留下一個(gè)稱為僵尸進(jìn)程(Zombie)的數(shù)據(jù)結(jié)構(gòu),等待父進(jìn)程處理。這是每個(gè)子進(jìn)程在結(jié)束時(shí)都要經(jīng)過的階段。如果子進(jìn)程在exit()之后,父進(jìn)程沒有來得及處理,這時(shí)用ps命令就能看到子進(jìn)程的狀態(tài)是”Z”。如果父進(jìn)程能及時(shí) 處理,可能用ps命令就來不及看到子進(jìn)程的僵尸狀態(tài),但這并不等于子進(jìn)程不經(jīng)過僵尸狀態(tài)。
如果父進(jìn)程在子進(jìn)程結(jié)束之前退出,則子進(jìn)程將由init接管。init將會以父進(jìn)程的身份對僵尸狀態(tài)的子進(jìn)程進(jìn)行處理。
另外,還可以寫一個(gè)php文件,然后在以后臺形式來運(yùn)行它,例如:
<?php //Action代碼 public function createAction(){ //.... //將args替換成要傳給insertLargeData.php的參數(shù),參數(shù)間用空格間隔 system('php -f insertLargeData.php ' . ' args ' . '&'); $this->redirect('/'); } ?>
然后在insertLargeData.php文件中做數(shù)據(jù)庫操作。也可以用cronjob + php的方式實(shí)現(xiàn)大數(shù)據(jù)量的處理。
如果是在終端運(yùn)行php命令,當(dāng)終端關(guān)閉后,剛剛執(zhí)行的命令也會被強(qiáng)制關(guān)閉,如果你想讓其不受終端關(guān)閉的影響,可以使用nohup命令實(shí)現(xiàn):
<?php //Action代碼 public function createAction(){ //.... //將args替換成要傳給insertLargeData.php的參數(shù),參數(shù)間用空格間隔 system('nohup php -f insertLargeData.php ' . ' args ' . '&'); $this->redirect('/'); } ?>
你還可以使用screen命令代替nohup命令。
- PHP多進(jìn)程編程之僵尸進(jìn)程問題的理解
- 什么是PHP7中的孤兒進(jìn)程與僵尸進(jìn)程
- php 的多進(jìn)程操作實(shí)踐案例分析
- php 多進(jìn)程編程父進(jìn)程的阻塞與非阻塞實(shí)例分析
- php實(shí)現(xiàn)的簡單多進(jìn)程服務(wù)器類完整示例
- 深入解析PHP中的(偽)多線程與多進(jìn)程
- 深入探究PHP的多進(jìn)程編程方法
- PHP使用pcntl_fork實(shí)現(xiàn)多進(jìn)程下載圖片的方法
- PHP 多進(jìn)程 解決難題
- 解析PHP實(shí)現(xiàn)多進(jìn)程并行執(zhí)行腳本
- PHP基于文件鎖解決多進(jìn)程同時(shí)讀寫一個(gè)文件問題示例
- php多進(jìn)程并發(fā)編程防止出現(xiàn)僵尸進(jìn)程的方法分析
相關(guān)文章
詳解no input file specified 三種解決方法
這篇文章主要介紹了詳解no input file specified 三種解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-11-11thinkphp5框架實(shí)現(xiàn)的自定義擴(kuò)展類操作示例
這篇文章主要介紹了thinkphp5框架實(shí)現(xiàn)的自定義擴(kuò)展類操作,結(jié)合實(shí)例形式簡單分析了thinkPHP5在extend目錄下建立自定義擴(kuò)展類的具體操作步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2019-05-05php項(xiàng)目中百度 UEditor 簡單安裝調(diào)試和調(diào)用
這篇文章主要介紹了php項(xiàng)目中百度 UEditor 簡單安裝調(diào)試和調(diào)用的相關(guān)資料,需要的朋友可以參考下2015-07-07ThinkPHP實(shí)現(xiàn)生成和校驗(yàn)驗(yàn)證碼功能
這篇文章主要為大家詳細(xì)介紹了ThinkPHP實(shí)現(xiàn)生成和校驗(yàn)驗(yàn)證碼功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04zen_cart實(shí)現(xiàn)支付前生成訂單的方法
這篇文章主要介紹了zen_cart實(shí)現(xiàn)支付前生成訂單的方法,結(jié)合實(shí)例形式詳細(xì)分析了zen_cart支付前生成訂單的具體步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-05-05