应用场景:比如在浏览一些页面时,可以让浏览器提前得到响应,然后通过创建子进程来执行异步代码(比如:统计数累加、来源统计等)
DEMO:
public function Test1Action(){
echo "string";
$this->asyncode(function(){
sleep(3);
$mongo = Mongo_Db::getInstance();
$colle = $mongo->collection('TEST');
$colle->insert(array('time'=>time(),'value'=>'异步进程'));
});
return false;
}
异步方法:
/**
* 异步执行代码
* 需要安装pcntl扩展,若没有安装则是同步执行。
* 注意:此方法要在最后调用,否则程序意外的exit后可能会产生僵尸进程!
* @param $fun 函数对象
* @param $istest true:改为同步调试 false:异步执行(默认)
* @example
* $this->asyncode(function(){
* sleep(2);
* file_put_contents('xiaocai.txt', time());
* });
*/
private function asyncode($fun,$istest=false){
//验证扩展
if(!function_exists('pcntl_fork') || !function_exists('posix_kill')){
$fun();
return false;
}
//调试模式
if($istest){
$fun();
return true;
}
//创建进程
$pid = pcntl_fork();
if($pid == -1){
throw new Exception(' couldn\'t fork.');
}else{
if($pid){
//父进程执行
$status = 0;
pcntl_wait($status); //阻塞子进程等待响应(防止僵尸进程)
if($status!=0){/*子进程异常退出*/}
exit;
}else{
//子进程执行
$gpid = pcntl_fork();
if( $gpid < 0){
throw new Exception(' couldn\'t fork.');
}elseif($gpid > 0){
//孙进程执行
$status = 0;
$status = pcntl_wait($status,1); //孙进程非阻塞异步执行
if($status!=0){/*孙进程异常退出*/}
posix_kill(getmypid(), SIGTERM); //kill掉子进程
exit(0);
}else{
//孙进程执行
@$fun();
posix_kill(getmypid(), SIGTERM); //kill掉孙进程
exit(0);
}
}
}
}
方法中通过两次pcntl_fork()来避免产生僵尸进程,可以使用以下方法处理僵尸进程.
查找僵尸进程
ps -el | grep php
杀死僵尸进程
ps -A -o stat,ppid,pid,cmd | grep -e '^[Zz]' | awk '{print $2}' | xargs kill -9
(kill -9 父进程id)