zoukankan      html  css  js  c++  java
  • PHP多进程初步

    一、前言

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

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

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

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

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

    二、创建子进程

    使用 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 退出,防止子进程进入循环。

    三、管理子进程

    管理子进程,使用的是信号。简单来说,就是父进程里使用两个函数 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:进程终止

    四、处理子进程

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

    // 向进程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 的子进程状态,如果指定的子进程在此函数调用时已经退出(俗称僵尸进程),此函数将立刻返回,并释放子进程的所有系统资源,此进程可以避免子进程变成僵尸进程,造成系统资源浪费。这样就可以实现跟子进程共同完成的任务的目的了。

    五、实例

    如果一个任务被分解成多个进程执行,就会减少整体的耗时。比如有一个比较大的数据文件要处理,这个文件由很多行组成。如果单进程执行要处理的任务,量很大时要耗时比较久。这时可以考虑多进程。多进程处理分解任务,每个进程处理文件的一部分,这样需要均分割一下这个大文件成多个小文件(进程数和小文件的个数等同就可以)。

    比如文件 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
    ";
        } 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
    ";
    }

    参考:

    《php多进程总结》:https://www.cnblogs.com/leezhxing/p/5223289.html

    《初探PHP多进程》:https://www.cnblogs.com/zhenbianshu/p/5676822.html

    《PHP利用多进程处理任务》:https://www.cnblogs.com/firstForEver/p/7301630.html

  • 相关阅读:
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》内容介绍
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》前言
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》内容介绍
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》微软中国.NET Micro Framework项目组工程师所作之序
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》资源汇总
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》微软中国.NET Micro Framework项目组工程师所作之序
    《玩转.NET Micro Framework 移植基于STM32F10x处理器》前言
    Windows、Linux、ARM、Android、iOS全平台支持的RTMP推流组件libEasyRTMP库接口调用说明
    简单高效易用Windows/Linux/ARM/Android/iOS平台实现RTMP推送组件EasyRTMPAndroid MediaCodec硬编码流程介绍
    RTSP网络监控摄像头如何实现Windows、Linux、ARM、Android、iOS全平台支持的拉RTSP流推出RTMP直播流?
  • 原文地址:https://www.cnblogs.com/tangxuliang/p/9208133.html
Copyright © 2011-2022 走看看