PHP是一门较早出现的WEB开发脚本语言,并由于其语法结构简单、易学、开源等特性迅速占领WEB开发脚本语言领域,并成为这个领域的龙头老大直至今日。PHP从一出生就被设计用来快速开发WEB应用,这也注定了它在某些方面的先天不足,例如在cli环境下处理大量数据的情况,或者在并发编程方面,都显得力不从心。
本文主要讲解基于PCNTL的PHP并发编程,虽然PHP本身不支持多进程,但基于LINUX的PHP扩展PCNTL却可以提供多进程编程。网络上很多同类文章,但笔者进行多次尝试后发现,不是难以控制进程数量,就是有潜在产生僵尸进程或孤儿进程的危险,或者父进程阻塞难以获得更大的并发效果,且大多没有介绍FORK的原理,使得PHP程序员学习PCNTL并发编程尤为困难。本文力求解决这个问题。
FORK编程的大概原理是,每次调用fork函数,操作系统就会产生一个子进程,儿子进程所有的堆栈信息都是原封不动复制父进程的,而在fork之后,父进程与子进程实际上是相互独立的,父子进程不会相互影响。也就是说,fork调用位置之前的所有变量,父进程和子进程是一样的,但fork之后则取决于各自的动作,且数据也是独立的;因为数据已经完整的复制给了子进程。而唯一能够区分父子进程的方法就是判断fork的返回值。如果为0,表示是子进程,如果为正数,表示为父进程,且该正数为子进程的PID(进程号),而如果是-1,表示子进程创建失败。请看如下代码:
<?php $pid = pcntl_fork(); switch($pid){ case -1: echo "couldn't fork"; break; case 0: echo "I'm parent"; break; default : echo "I'm child"; } ?>
以上代码会产生一个子进程,并根据返回的pid进行父子进程的判断。
生产环境中以上代码大多是不适用的,我们需要大量的并发进程为同时为我们处理事情。这时,我们就需要fork多次,而产生的子进程数量需要在我们的控制之中,否则无限制的fork只会拖垮服务器。笔者曾经有过经历,几秒钟服务器负载从0.3左右飙到800多,吓的一身冷汗。
而子进程的使用通常会涉及到两种:子进程执行完任务直接退出;子进程常驻内存,等待任务。以上两种方式适用于不同情况。第一种情况大多我们不需要考虑太多,除非子进程的创建是循环进行的。而第二种则需要考虑进程间通信。
无论哪一种,无可避免的一个问题就是僵尸进程。僵尸进程就是子进程退出后,父进程没有及时回收,系统仍然保留子进程的执行信息(例如PID,退出状态等),留待其他程序读取。如果僵尸进程数量很少,我们可以忽略掉。但如果是在一个循环中fork(并发编程中常见的死循环),这个问题就不能无视了,父进程必须定期回收已经退出的子进程。子进程的回收我们采用pcnt_wait函数来完成。如下面这段代码:
<?php while(true){ $pid = pcntl_fork(); switch($pid){ case -1: echo "couldn't fork"; break; case 0:{ $subPid = pcntl_wait($status); var_dump($status); break; } default : echo "I'm child"; exit(0); } }
以上代码能够循环产生子进程,并且父进程会阻塞等待子进程退出,这样就产生了一个问题,父进程必须等待一个子进程退出后,再创建另外一个。额,这还是串行执行的不是吗?是的,解决办法就是将pcntl_wait函数替换成pcntl_waitpid()并添加WNOHANG参数。该函数可以在没有子进程退出的情况下立刻跳出执行后续代码。能够达到更好的并发效果。具体代码这里不再演示。
循环创建子进程是一件非常浪费操作系统资源的事情。既然使用了死循环来处理任务,那么就说明任务是一个可以队列化的数据结构。我们可以采用进程间的通信,解决子进程退出重建的问题。而通信的机制主要有信号量、管道、共享内存等。然后我们需要一个生产者和消费者的模型。而基于fork的这种代码编写方式,非常不利于我们编写复杂的业务逻辑。所以建议将进程控制与业务处理的代码进程抽象隔离。进程间通信本文暂不涉及,如果读者有需要可以阅读关于管道、共享内存和信号量的文章。
根据上面所说,循环创建子进程会造成系统资源的浪费,而循环创建往往意味着任务可以队列化。我们可以创建子进程后,让子进程常驻内存,持续执行等待任务到达。而这类模型往往可以用生产-消费模型来实现。生产者负责将任务写入队列,而子进程从队列中取出任务并执行。队列的实现最好采用本身支持互斥的方式,这样可以降低代码的复杂度,管道是个不错的选择。
基于fork方式实现的多进程,由于我们只能使用Pid来做代码隔离,所以进程控制中会充斥的各种if、else或者switch。这对实生产者和消费者模型造成一定难度。以下是一个生产者消费者的模型:
<?php /** * @author:Jenner * @date 2014-01-14 */ class JetMultiProcess { //最大队列长度 private $size; private $curSize; //生产者 private $producer; //消费者 private $worker; /** * 构造函数 * @param string $worker 需要创建的消费者类名 * @param int $size 最大子进程数量 * @param $producer 需要创建的消费者类名 */ public function __construct($producer, $worker, $size=10){ $this->producer = new $producer; $this->worker = $worker; $this->size = $size; $this->curSize = 0; } public function start(){ $producerPid = pcntl_fork(); if ($producerPid == -1) { die("could not fork"); } else if ($producerPid) {// parent while(true){ $pid = pcntl_fork(); if ($pid == -1) { die("could not fork"); } else if ($pid) {// parent $this->curSize++; if($this->curSize>=$this->size){ $sunPid = pcntl_wait($status); $this->curSize--; } } else {// worker $worker = new $this->worker; $worker->run(); exit(); } } } else {// producer $this->producer->run(); exit(); } } }
以上代码,通过size控制多进程数量,通过构造函数传入生产者和消费者的类型。父进程第一次fork产生一个子进程生产者,然后再进行size次fork创建多个消费者。类似方法可以创建多个生产者和多个消费者协同工作。生产者和消费者都必须实现run方法,并在run方法中创建死循环。循环写入和读取队列进行协同工作。该类没有提供进程间通信的功能。通信需要在生产者和消费者类中实现。这样能够使得进程控制的代码看起来更加简洁。
该模型已经应用于大型项目,每天承载数万次的任务写入。
相关推荐
本篇文章将深入探讨如何基于PCNTL封装一个PHP并发处理类,并了解相关的知识点。 首先,让我们理解PCNTL扩展的核心功能。PCNTL提供了一系列的函数,如`pcntl_fork()`用于创建新的进程,`pcntl_wait()`或`pcntl_...
以下将详细分析防止PHP多进程并发编程出现僵尸进程的几种方法。 **方法一:父进程等待子进程结束** 通过`pcntl_wait`和`pcntl_waitpid`这两个函数,父进程可以阻塞地等待子进程的退出,从而在子进程结束后进行资源...
总结起来,`saptie/async`库通过封装`PCNTL`扩展,为PHP开发者提供了一种简单的方式来实现异步和并行执行代码,提升了代码执行效率,特别是在处理并发任务时。利用`Pool`和`Promise`,你可以轻松地管理和控制异步...
PHP的pcntl扩展是一个非常强大的功能,它可以用来创建和管理多个进程,这对于需要进行大量并发处理的应用程序来说是极其有用的。...因为并发编程往往涉及复杂的状态管理,对程序员来说也是一大挑战。
`pcntl`(Process Control)是PHP的一个核心扩展,它允许开发者在PHP中实现多进程编程,这对于处理并发任务、后台任务或者在服务器端进行负载平衡等场景非常有用。然而,需要注意的是,`pcntl`扩展只在类Unix系统上...
PHP多进程编程是一种在PHP中实现并发处理的技术,它允许程序同时执行多个操作。在PHP中,多进程编程可以通过pcntl扩展实现,而pcntl_fork函数是这个扩展中最为核心的函数之一。本文将详细介绍pcntl_fork的使用方法和...
在PHP编程中,多进程(Multi-Process)是一种利用操作系统资源进行并发处理的方式,它可以将一个大任务分解为多个小任务并行执行,提高程序的运行效率。在PHP中,可以借助`pcntl`(Process Control)扩展来实现多...
在PHP编程中,有时我们需要处理耗时较长的任务,如大量数据处理或批量下载资源,这时可以...总的来说,`pcntl_fork()`提供了一种强大的工具,使PHP开发者能够充分利用Linux系统的多核处理器资源,实现高效的并发处理。
该压缩包文件“基于PHP的AJAX多进程批量Ping工具源码.zip”包含了一个使用PHP编程语言开发的AJAX多进程批量Ping...对于Web开发者来说,研究和理解这个源码可以帮助提升对PHP并发处理、AJAX通信以及网络诊断技术的理解。
为了提高处理能力,PHP提供了多进程编程的能力,通过`pcntl_fork()`函数可以创建子进程,实现并发处理。 **子进程的创建** 创建子进程的基本步骤如下: 1. 调用`pcntl_fork()`函数。如果调用成功,它将在父进程中...
在PHP中进行多进程编程是通过一组特定的进程控制函数(PCNTL)来实现的,这些函数允许开发者创建和管理子进程,处理信号,并在*nix系统中模拟C语言中的进程控制功能。多进程编程在处理并发任务,如数据采集、邮件...
在PHP中,使用pcntl_fork函数创建子进程是实现多进程编程的一个重要方式。这个功能可以使PHP程序并行处理多个任务,提高程序的执行效率和处理能力。pcntl_fork是PHP的Process Control扩展提供的一个函数,它可以让一...
首先,`pcntl`扩展在PHP中的作用主要是支持多进程编程。在上面的例子中,`newChild`函数用于创建新的子进程。通过`pcntl_fork()`函数,可以创建一个与父进程几乎完全相同的副本,即子进程。如果`pcntl_fork()`成功,...
4. **并发与异步编程**:虽然PHP不是原生支持并发和异步操作的语言,但可以通过`pthreads`扩展或配合其他非阻塞I/O库(如libevent、libuv)来实现。`pthreads`扩展允许在PHP中创建线程,不过此扩展仅适用于ZTS(Zend...
【标题】中的“基于PHP的PTphp小说搜索抓取优化程序源码”表明这是一个使用PHP编程语言开发的小说搜索引擎优化程序。PHP(Hypertext Preprocessor)是一种广泛使用的开源脚本语言,尤其适用于Web开发,可以嵌入到...
在PHP中,由于其默认的执行模型是基于请求-响应的,即每个HTTP请求都会启动一个新的PHP进程,处理完请求后立即结束,这并不适合处理需要长期运行的任务。而EasyTask通过常驻内存的方式解决了这个问题,它能够在内存...
- 使用`pcntl`扩展进行多进程编程时,需要注意进程的同步问题,确保父进程能正确回收子进程资源,避免产生僵尸进程。 - 对于多子进程的创建和管理,应合理安排父进程对子进程的等待时机,避免程序逻辑的错误。 - 在...
总的来说,面向对象的多进程管理器是PHP进行并发编程的强大工具,它通过抽象和封装降低了使用门槛,使得开发者能够更高效地利用系统资源,处理复杂的并发任务。对于那些需要处理大量并发请求或进行后台任务调度的PHP...