php异步执行的几种方法, 非阻塞运行(详细介绍和总整理)

本文详细介绍了PHP中实现异步执行的pcntl_fork方法,该方法需要扩展支持,但可以实现非阻塞运行。同时,文章也邀请读者分享更多关于PHP异步执行的优秀解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  - 1.提前结束会话(请求), 但是进程仍在执行后续耗时代码
    - FastCGI模式下, 使用fastcgi_finish_request()函数能马上结束会话
    
      - 注意: fastcgi_finish_request官方介绍页面下的评论提出需要注意的点[链接](https://www.php.net/manual/zh/function.fastcgi-finish-request.php)
        正常脚本结束时php会自动调用session_write_close()函数, 而脚本在处理中的时候占用者session锁,对于后续请求来说是阻塞的.所以要尽快手动调用session_write_close()结束并保存session数据. 这对于其他有竞争锁情况同样适用,没有用了要尽快释放
        
    - 一般模式下(如Apache, Nginx, FastCGI(直接使用fastcgi_finish_request()更方便等), 提前输出内容, 结束会话
      
      <?php
      //适用于大多数运行模式(不包括命令行模式)
      set_time_limit(0);  //设置不限执行时间
      ignore_user_abort(true);  //忽略客户端中断
      //nginx等可能需要达到4k才会输出buffer,所有先输出一些空字符串
      $str = str_repeat(' ', 65536);
      $str .= '立即输出' . date('Y-m-d H:i:s');
      #header('X-Accel-Buffering: no');   // 关闭加速缓冲, 在nginx模式需要开启此行
      header("Content-Type: text/html;charset=utf-8");
      header("Connection: close");//告诉浏览器不需要保持长连接
      header('Content-Length: '. strlen($str));//告诉浏览器本次响应的数据大小只有上面的echo那么多
      ob_end_flush();
      ob_start();
      echo $str;
      ob_flush();
      flush();
      //至此,连接已经关闭. 但是进程还不会结束, 以下程序还能运行但不会输出
      sleep(10);
      file_put_contents('./log.txt', '10s后我写入log文本: 时间' . date('Y-m-d H:i:s'));
      
      - 注意: 在以下情况中,该方法失效:无论那个模式,gzip一定要关闭; 是window32下web服务不行;   [官方说明](https://www.php.net/manual/zh/function.flush.php)
      
        个别web服务器程序,特别是Win32下的web服务器程序,在发送结果到浏览器之前,仍然会缓存脚本的输出,直到程序结束为止。
        
        有些Apache的模块,比如mod_gzip,可能自己进行输出缓存,这将导致flush()函数产生的结果不会立即被发送到客户端浏览器。
      
      
  - 2.请求子进程的网址, 不等待返回结果(说白了就是发个请求唤起耗时的子脚本, 而不等待这个耗时子脚本返回结果)
    - fsockopen打开一个网络连接或者一个Unix套接字连接, 忽略返回结果(不等待返回结果)
    - curl 设置超时时间为1s, 忽略返回结果(不等待返回结果,直接超时,但最短要1s)
  
  - 3.开启子进程(确保以下函数没有被禁用)
    - popen 
      >[官方介绍链接](https://www.php.net/manual/zh/function.popen.php): 打开一个指向进程的管道,该进程由派生给定的command命令执行而产生。他是单向的(只能用于写或读)
      
      - 例子1 (不等待子进程返回结果直接结束父脚本)
      <?php
      ini_set('date.timezone', 'Asia/shanghai');
      echo "父脚本开始\n";
      echo date('Y-m-d H:i:s') . "\n";
      //&: 转入后台运行; nohup:不挂断地运行命令, 防止当前的终端窗口被关闭后导致进程结束
      $cmd = 'nohup php cmd.php &';
      pclose(popen($cmd, 'r'));//开启一个子进程后马上关闭, 子进程进入后台处理耗时的处理
      echo "父脚本结束";
       
      - 例子2 (等待子进程返回结果在结束父脚本)
      <?php
      ini_set('date.timezone', 'Asia/shanghai');
      echo "父进程开始\n";
      echo date('Y-m-d H:i:s') . "\n";
      $cmd1 = 'nohup php cmd.php &';
      $cmd2 = 'nohup php cmd1.php &';     
      //执行耗时异步任务1
      $p1 = popen($cmd1, 'r');
      //执行耗时异步任务2, 与任务1是并行运行的
      $p2 = popen($cmd2, 'r');
      register_shutdown_function(function () use ($p1, $p2) {
          $res1 = stream_get_contents($p1);
          echo $res1;
          $res2 = stream_get_contents($p2);
          echo $res2;
          pclose($p1);
          pclose($p2);
          echo "父脚本结束" . date('Y-m-d H:i:s') . "\n";
      });
      
    - proc_open 开启异步子进程
      >与popen()一样, 只是该函数有更强的控制程序执行的能力, 可以双向(读又写)
      >参考例子(https://my.oschina.net/eechen/blog/745504)

         -  pcntl_fork  需要扩展支持,比较麻烦

- 4.借助框架如swoole等

有其他更好的方式欢迎补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值