31
2014
08

#学习笔记#php多线程

首先要说的是,php本身是不支持多线程的。

但是很多时候,单线程并不能满足所需的效率。比如说很多采集程序,如果只靠单线程来处理,效率可想而知。

所以就有了我们接下来要讲的东西,严格意义上来说,应该叫php的伪多线程。

php实现伪多线程的方法也有挺多的,这里我们说的是一个php扩展--pcntl。

  1. pcntl安装说明

在PHP中进程控制支持默认是关闭的。如果您还没有安装你的php环境,可以使用 --enable-pcntl可以安装上该扩展。

如果你已经安装好了,那只有用另个的方法了。这里有说明,就不表了。http://php.net/manual/zh/pcntl.installation.php

注意:很遗憾的要说,非Unix类系统不支持该扩展

2.pcntl使用及问题

php官方网站给了个例子

<?php

$pid = pcntl_fork();
//父进程和子进程都会执行下面代码
if ($pid == -1) {
    //错误处理:创建子进程失败时返回-1.
     die('could not fork');
} else if ($pid) {
     //父进程会得到子进程号,所以这里是父进程执行的逻辑
     pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
} else {
     //子进程得到的$pid为0, 所以这里是子进程执行的逻辑。
}

?>

嗯,就是这样,看起来挺简单的,其实也挺简单。不过有个问题,两个进程虽然独立了,但是有很强的依赖,父进程必须等待子进程结束后才能结束,要不然就会产生僵死进程。而有时候,我们往往需要能够得到快速的响应。

比如一个问答网站的提问功能,他要实现很多逻辑。记录问题,生成全站feed,向好友发送通知,处理是否有@某人,若有也发送提醒等等。这一堆下来,其实只要保证帖子写到表里去了,其他步骤对于准确性要求并不是很好,用户可能只关心帖子发成功了就可以,至于有没有发通知,有没有生成Feed他并不是很关注。

那我就想能不能把记录问题这个放在主线程,执行完之后立即返回结果,子线程继续做后续处理。对不等待,去掉pcntl_wait不就好了?答案当然是否定的。每个进程在退出的时候,都会释放掉资源,但是并不是所有,仍会有一些保留信息,比如进程号pid 退出状态 运行时间等(即成为了僵死进程),这些进程的资源直到父进程结束之后,这部分资源才会被释放掉。而apache是多线程的,所以其进程不会自动结束,即父进程就不会自动终止,那就意为着,僵死进程资源没有了释放的途径。而且你悲剧的发现,僵死进程kill都kill不掉,只能重启apache。


说了这么说,那是不是没办法了?答案当然也是否定的。

同样的在产生僵尸进程的那种情况下,即子进程结束了但父进程还在继续运行(并未调用wait/waitpid)这段期间,如果异常终止了,那么该子进程就会自动被init接管。那么它就不再是僵尸进程了。

父进程异常终止了,子进程就会被init接管,而不再是僵死进程。那么可不可以让上面提到的子进程再产生个子进程呢?即孙子进程,让孙子进程去处理耗时的业务逻辑,子进程直接终止,那么主进程是不是就不用等那么长时间了?

嗯,是的,就是这样。下面是考熟成熟的完整版。请看代码

if(!function_exists('pcntl_fork')){
    //当前系统不支持或者未完装pcntl扩展
    return ;
}

$pid = pcntl_fork();
if ($pid == -1) {
	//没有办法,不好使那就只能在这继续处理了
} else if ($pid) {
	pcntl_waitpid($pid,$status,WUNTRACED);//等待子进程结束,防止成僵死进程
	log_message('debug', '主线程执行完成.'.time().'主线线id'.posix_getpid().',子线程'.$pid);
} else {//子进程
	log_message('debug', '子进程'.getmypid().'开始创建孙进程');
	$pid = pcntl_fork();
	if($pid == -1){
		log_message('debug', '创建孙子进程失败');
		exit(-1);
	}else if($pid == 0){
		sleep(2);//这里小憩一下,让子进程(孙子进程的父进程)先死
		//posix_setsid();
		log_message('debug', '孙进程开始处理业务逻辑.');
		//业务逻辑在这

		log_message('debug', '孙进线程执行完成.'.time().'孙进程id'.posix_getpid());
		posix_kill(posix_getpid(),SIGTERM);
		//exit(0);
	}else{
		log_message('debug', '子进程直接退出'.posix_getpid());//子进程退出,把孙子进程交给系统托管
		posix_kill(posix_getpid(),SIGTERM);
		//exit(0);
	}
}


疑问:这里有个不解,不明白为什么一定要kill,exit退出为啥子不能结束进程?若你知道,告诉我一下,谢谢。



« 上一篇下一篇 »

相关文章: