参考 基于PCNTL的PHP并发编程

PCNTL 是 PHP 中的一组进程控制函数,可以用来 fork(创建)进程,传输控制信号等。

在PHP中,进程控制支持默认关闭。编译时通过 --enable-pcntl 配置选项可以使 PHP的 CGI 或 CLI 版本打开进程控制支持(但是注意不要在 web 服务器中用 PCNTL,会导致不可预料的问题)。仅 Unix 类系统支持 PCNTL 模块。

PCNTL 原理

简单示例

<?php

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

原理

PCNTL 中的一系列函数,都对应操作系统中的函数,例如 pcntl_fork 就对应 Linux 系统中的 fork 函数。

每次在进程中调用 fork 函数时,操作系统会把进程完整的复制一份,作为这个进程的子进程。此时,两个进程的唯一区别就是 PID(进程ID)和 PPID(父进程ID)。根据 fork 返回值的不同可以判断当前是在父进程还是子进程中,从而可以有不同的处理逻辑。

为了保证系统资源利用率,必须防止出现僵尸进程(进程结束后未回收资源)或孤儿进程(父进程提前结束),可以通过 pcntl_wait 或 pcntl_waitpid 函数来监控子进程。

常用函数

pcntl_fork

在当前进程的当前位置产生分支(子进程),父进程和子进程都从fork的位置开始向下继续执行,不同的是父进程执行过程中,得到的fork返回值为子进程号,而子进程得到的是0。子进程仅PID(进程号) 和PPID(父进程号)与其父进程不同。

语法:

int pcntl_fork ( void )

返回值:

  • 成功时,产生进程分支:

    • 在父进程执行线程内返回产生的子进程的PID
    • 在子进程执行线程内返回0
  • 失败时,在 父进程上下文返回-1,不会创建子进程,并且会引发一个PHP错误。

pcntl_waitpid

挂起当前进程的执行,直到参数pid指定的进程号对应的进程退出, 或接收到一个信号要求中断当前进程或调用一个信号处理函数。

如果pid指定的子进程在此函数调用时已经退出(僵尸进程),此函数将立刻返回。

语法:

int pcntl_waitpid ( int $pid , int &$status [, int $options = 0 ] )

返回值:

  • 正常执行时返回退出的子进程进程号
  • 发生错误时返回-1,如果提供了 WNOHANG作为option(wait3可用的系统)并且没有可用子进程时返回0。

pcntl_wait

挂起当前进程,直到当前进程的一个子进程退出或接收到一个信号要求中断当前进程或调用一个信号处理函数。如果当前进程的一个子进程在调用此函数时已经退出(俗称僵尸进程),此函数立刻返回。子进程使用的所有系统资源将被释放。

pcntl_wait 等同于以-1作为参数pid 的值并且没有options参数来调用pcntl_waitpid() 函数。

示例

多子进程实现并发

多子进程时,需要注意避免僵尸进程,浪费资源:

<?php

$procNum = 3;
for($i = 0; $i < $procNum; $i++){
$nPID = pcntl_fork();//创建子进程
if ($nPID == 0){
work();
exit(0);
} elseif ($nPID == -1) {
die('could not fork');
} else {
// 如果在这里写 pcntl_wait($status),则只有当前进程执行完毕后才会创建下一个进程
}
}
// 父进程会执行下面的代码,等待子进程执行完毕,避免僵尸进程
$n = 0;
while ($n < $procNum) {
$nStatus = -1;
$nPID = pcntl_wait($nStatus);
if ($nPID > 0) {
++$n;
}
} function work(){
while(true){
$nPID = pcntl_fork();//创建子进程
if ($nPID == 0){
echo time();
exit(0);
}
pcntl_waitpid($nPID,$nStatus);
}
}

进程状态:

[root@VM_139_38_centos ~]# ps -ef | grep php
root 26355 24175 0 23:17 pts/0 00:00:00 php pcntl2.php
root 26356 26355 3 23:17 pts/0 00:00:00 php pcntl2.php
root 26357 26355 5 23:17 pts/0 00:00:00 php pcntl2.php
root 26358 26355 3 23:17 pts/0 00:00:00 php pcntl2.php
root 27078 24819 0 23:17 pts/4 00:00:00 grep --color=auto php
root 27079 26357 0 23:17 pts/0 00:00:00 php pcntl2.php
root 27080 26358 0 23:17 pts/0 00:00:00 php pcntl2.php
root 27081 26356 0 23:17 pts/0 00:00:00 php pcntl2.php

单子进程

<?php

$pid = pcntl_fork();

if ($pid == -1) {
die('could not fork');
} elseif ($pid) {
pcntl_wait($status);
} else {
while(1) {
sleep(1);
echo time();
}
}

最新文章

  1. Maven安装配置使用
  2. MVC 随记
  3. 转:jQuery.fn.extend与jQuery.extend到底区别在哪?
  4. sharepoint 2013 安装配置PowerView
  5. Nyoj 城市平乱(图论)
  6. 老李分享:大数据框架Hadoop和Spark的异同
  7. 2.跑nodejs文件
  8. 201521123023《Java程序设计》第14周学习总结
  9. Oracle触发bug(cursor: mutex S),造成数据库服务器CPU接近100%
  10. js前后五年的时间日期万年历
  11. 恢复win7快捷方式小箭头
  12. 【BZOJ1023】仙人掌图(仙人掌,动态规划)
  13. Oracle EBS OPM 发放生产批
  14. TiKV 源码解析系列 - Raft 的优化
  15. 【LeetCode OJ】Search Insert Position
  16. miniui dataGrid drawcell事件
  17. Taxi Cab Scheme UVALive - 3126 最小路径覆盖解法(必须是DAG,有向无环图) = 结点数-最大匹配
  18. Smobiler实现扫描条码和拍照功能(开发日志八)
  19. android 应用间共享数据,调用其他app数据资源
  20. 几乎零配置产生Nuget包的库:White Tie

热门文章

  1. 使用pdfjs插件在线预览PDF文件
  2. Android--ViewPager点击按钮切换下一页
  3. [七月挑选]frp初使用
  4. wpf textblock 长文本滚动
  5. Linux系统性能测试工具(五)——磁盘io性能工具之fio
  6. ise和quartus共用仿真软件
  7. padding溢出
  8. P4315 月下“毛景树” (树链剖分+边剖分+区间覆盖+区间加+区间最大值)
  9. zookeeper之二 zkCli客户端命令
  10. php实现大文件上传带进度条