PHP多进程初步

1、前言

咱们都知道PHP是单线程执行,处理多并发主要是依赖服务器或PHP-FPM的多进程及它们进程的复用,但PHP实现多进程也意义重大,尤为是在后台Cli模式下处理大量数据或运行后台DEMON守护进程时。不能应用在Web服务器环境。php

/** 检测是否CLI模式,确保这个函数只能运行在SHELL中 */
if (substr(php_sapi_name(), 0, 3) !== 'cli') {
  die("cli mode only");
}

平常任务中,有时须要经过php脚本执行一些日志分析,队列处理等任务,当数据量比较大时,可使用多进程来处理。html

PHP的多线程也曾被人说起,但进程内多线程资源共享和分配的问题难以解决。PHP也有多线程想关的扩展 pthreads ,但听说不太稳定,且要求环境为线程安全,所用很少。shell

要实现PHP的多进程,须要安装 pcntl 和 posix 扩展。api

2、建立子进程

使用 pcntl_fork() 函数能够在当前位置产生分支。fork 是建立了一个子进程,父进程和子进程都从 fork 的位置开始向下继续执行,不一样的是父进程执行过程当中,获得的 fork 返回值为子进程号,而子进程获得的是0,执行失败则返回-1。安全

由于系统初始init进程的pid为1,后来的全部进程pid都会大于该进程,因此能够经过 pcntl_fork() 的返回值大于1来判断当前进程是父进程,返回值等于0来判断是子进程。服务器

$ppid = posix_getpid(); // 获取当前进程的id
$pid = pcntl_fork();  // 建立子进程
if ($pid == -1) {
    throw new Exception('fork子进程失败!');
} elseif ($pid > 0) {
    // 父进程执行逻辑
    cli_set_process_title("我是父进程,个人进程id是{$ppid}.");
    sleep(30); 
    pcntl_wait($status); //等待子进程中断,防止子进程成为僵尸进程。
} else {
    // 子进程执行逻辑
    $cpid = posix_getpid();
    cli_set_process_title("我是{$ppid}的子进程,个人进程id是{$cpid}.");
    sleep(30);
}

执行结果:多线程

注意:若是是在循环中建立子进程,那么子进程中最后要 exit 退出,防止子进程进入循环。并发

3、管理子进程

管理子进程,使用的是信号。简单来讲,就是父进程里使用两个函数 pcntl_signal() 和 pcntl_signal_dispatch(),负责给子进程安装信号处理器和分发工做。异步

在计算机科学中,信号是Unix、类Unix以及其余POSIX兼容的操做系统中进程间通信的一种有限制的方式。它是一种异步的通知机制,用来提醒进程一个事件已经发生。

咱们经过在父进程接收子进程传来的信号,判断子进程状态,来对子进程进行管理。咱们须要在父进程里使用 pcntl_signal() 函数和 pcntl_signal_dispatch() 函数来给各个子进程安装信号处理器:函数

// 安装一个信号处理器,$signo是待处理的信号常量,callback是其处理函数
pcntl_signal (int $signo , callback $handler) 
// 调用每一个等待信号经过pcntl_signal()安装的处理器 pcntl_signal_dispatch ()

PHP内常见的信号常量有:

  • SIGCHLD:子进程退出成为僵尸进程会向父进程发送此信号
  • SIGHUP:进程挂起
  • SIGTEM:进程终止

4、处理子进程

处理子进程,须要两个函数:

// 向进程id为$pid的进程发送$sig信号
bool posix_kill ( int $pid, int $sig )
//挂起当前进程的执行直到进程号为$pid的进程退出(若是$pid为-1,则等待任意一个子进程) int pcntl_waitpid ( int $pid, int &$status [, int $options = 0 ] )

posix_kill() 函数经过向子进程发送一个信号来操做子进程,在须要要时能够选择给子进程发送进程终止信号来终止子进程;

pcntl_waitpid() 函数等待或返回 fork 的子进程状态,若是指定的子进程在此函数调用时已经退出(俗称僵尸进程),此函数将马上返回,并释放子进程的全部系统资源,此进程能够避免子进程变成僵尸进程,形成系统资源浪费。这样就能够实现跟子进程共同完成的任务的目的了。

5、实例

若是一个任务被分解成多个进程执行,就会减小总体的耗时。好比有一个比较大的数据文件要处理,这个文件由不少行组成。若是单进程执行要处理的任务,量很大时要耗时比较久。这时能够考虑多进程。多进程处理分解任务,每一个进程处理文件的一部分,这样须要均分割一下这个大文件成多个小文件(进程数和小文件的个数等同就能够)。

好比文件 file.log 有10万行数据,如今想分4个进程处理。须要分割2.5万行一个文件。命令 split 能够作到:

<?php

shell_exec('split -l 25000 -d file.log prefix_name');

// 3个子进程处理任务
for ($i = 0; $i < 3; $i++){
    $pid = pcntl_fork();

    if ($pid == -1) {
        die("could not fork");
    } elseif ($pid) {
        echo "I'm the Parent $i\n";
    } else {// 子进程处理
        $content = file_get_contents("prefix_name0".$i);
        // 业务处理 begin

        // 业务处理 end
        exit; // 必定要注意退出子进程,不然pcntl_fork()会被子进程再fork,带来处理上的影响。
    }
}

// 等待子进程执行结束
while (pcntl_waitpid(0, $status) != -1) {
    $status = pcntl_wexitstatus($status);
    echo "Child $status completed\n";
}

参考:

《php多进程总结》:http://www.javashuo.com/article/p-agzwvxcg-bd.html

《初探PHP多进程》:http://www.javashuo.com/article/p-prjyxmqq-d.html

《PHP利用多进程处理任务》:http://www.javashuo.com/article/p-cqzqsddv-x.html

相关文章
相关标签/搜索