php不支持多线程,但是我们可以把问题转换成“多进程”来解决。由于php中的pcntl_fork只有unix平台才可以使用,所以本文尝试使用popen来替代。 
 
下面是一个例子:
  
被并行调用的子程序:

  1. <?php
  2. if($argc==1){
  3. echo("argv\n");
  4. }
  5. $arg = $argv[1];
  6. for($i=0; $i<10; $i++)
  7. {
  8. echo($i.".1.".time()." exec $arg \n");
  9. if($arg=='php2')
  10. {
  11. sleep(1);
  12. echo($i.".2.".time()." exec $arg \n");
  13. sleep(1);
  14. }else{
  15. sleep(1);
  16. }
  17. ?>

----------------------------
主调用者程序,由他调用子进程,同时并发的收集子程序的输出

  1. <?php
  2. error_reporting(E_ALL);
  3. $handle1 = popen('php sub.php php1', 'r');
  4. $handle2 = popen('php sub.php php2', 'r');
  5. $handle3 = popen('php sub.php php3', 'r');
  6. echo "'$handle1'; " . gettype($handle1) . "\n";
  7. echo "'$handle2'; " . gettype($handle2) . "\n";
  8. echo "'$handle3'; " . gettype($handle3) . "\n";
  9. //sleep(20);
  10. while(!feof($handle1) || !feof($handle2) || !feof($handle3) ){
  11. $read = fgets($handle1);
  12. echo $read;
  13. $read = fgets($handle2);
  14. echo $read;
  15. $read = fgets($handle3);
  16. echo $read;
  17. }
  18. pclose($handle1);
  19. pclose($handle2);
  20. pclose($handle3);
  21. ?>

-------------------

下面是我机器上的输出:

> php exec.php
'Resource id #4'; resource
'Resource id #5'; resource
'Resource id #6'; resource
0.1.1147935331 exec php1
0.1.1147935331 exec php2
0.1.1147935331 exec php3
1.1.1147935332 exec php1
0.2.1147935332 exec php2
1.1.1147935332 exec php3
2.1.1147935333 exec php1
1.1.1147935333 exec php2
2.1.1147935333 exec php3
3.1.1147935334 exec php1
1.2.1147935334 exec php2
3.1.1147935334 exec php3
4.1.1147935335 exec php1
2.1.1147935335 exec php2
4.1.1147935335 exec php3
5.1.1147935336 exec php1
2.2.1147935336 exec php2
5.1.1147935336 exec php3
6.1.1147935337 exec php1
3.1.1147935337 exec php2
6.1.1147935337 exec php3
7.1.1147935338 exec php1
3.2.1147935338 exec php2
7.1.1147935338 exec php3
8.1.1147935339 exec php1
4.1.1147935339 exec php2
8.1.1147935339 exec php3
9.1.1147935340 exec php1
4.2.1147935340 exec php2
9.1.1147935340 exec php3
5.1.1147935341 exec php2
5.2.1147935342 exec php2
6.1.1147935343 exec php2
6.2.1147935344 exec php2
7.1.1147935345 exec php2
7.2.1147935346 exec php2
8.1.1147935347 exec php2
8.2.1147935348 exec php2
9.1.1147935349 exec php2
9.2.1147935350 exec php2

**总结:**

**主程序循环等待子进程, 通过fgets或fread 把子进程的输出获取出来 , 从时间戳上看,的确实现了并发执行。**
  
-----------------------------------------------
改进:
  
*  popen打开的句柄是单向的,如果需要向子进程交互,可以使用proc_open
*  使用数组和子函数代替while(!feof($handle1) || !feof($handle2) || !feof($handle3) )这种龌龊的写法
*  用fread一次把子进程已经产生的输出取完,而不是每次一行。

这是另一个改进:
一个并发执行shell任务的调度者,本程序读取一个任务文件,把里面的每行命令并发执行, 可以设置同时存在的子进程数目:

  1. <?
  2. /*
  3. 主任务管理器
  4. 并发的执行子任务列表
  5. */
  6. include("../common/conf.php");
  7. include("../common/function.php");
  8. //开启的进程数
  9. $exec_number = 40 ;
  10. /***** main ********/
  11. if($argc==1){
  12. echo("argv\n");
  13. }
  14. $taskfile = $argv[1];
  15. //tasklist
  16. $tasklist = file($taskfile);
  17. $tasklist_len = count($tasklist);
  18. $tasklist_pos = 0;
  19. $handle_list = array();
  20. while(1){
  21. //子进程列表有空闲,则填充补齐子进程列表
  22. if($exec_number > count($handle_list) &&
  23. $tasklist_pos < $tasklist_len)
  24. {
  25. for($i=$tasklist_pos; $i<$tasklist_len; )
  26. {
  27. $command = $tasklist[$i] ;
  28. $handle_list[] = popen($command , "r" );
  29. tolog("begin task \t ".$tasklist[$i]);
  30. $i++;
  31. if($exec_number == count($handle_list)) break;
  32. }
  33. $tasklist_pos = $i;
  34. }
  35. //如果子进程列表空,退出
  36. if(0 == count($handle_list))
  37. {
  38. break;
  39. }
  40. //检查子进程列表的输出,把停掉的子进程关闭并记录下来
  41. $end_handle_keys = array();
  42. foreach($handle_list as $key => $handle)
  43. {
  44. //$str = fgets($handle, 65536);
  45. $str = fread($handle, 65536);
  46. echo($str);
  47. if(feof($handle))
  48. {
  49. $end_handle_keys[] = $key;
  50. pclose($handle);
  51. }
  52. }
  53. //踢出停掉的子进程
  54. foreach($end_handle_keys as $key)
  55. {
  56. unset($handle_list[$key]);
  57. //var_dump($handle_list);
  58. //exit;
  59. }
  60. }
  61. tolog("\n\n*******************end**********************\n\n", "" ,     true);
  62. ?>

尝试php命令行脚本多进程并发执行的更多相关文章

  1. linux shell并发执行命令

    一般我们在linux上十一shell命令的批量执行操作,一般使用for或者while 循环进行操作,但是这样有一个问题,for或者while本质上是串行的,并不能,如果某一个命令执行耗费的时间比较长, ...

  2. Linux Shell多进程并发以及并发数控制

    1. 基础知识准备 1.1. linux后台进程 Unix是一个多任务系统,允许多用户同时运行多个程序.shell的元字符&提供了在后台运行不需要键盘输入的程序的方法.输入命令后,其后紧跟&a ...

  3. python多进程并发

    由于Python下调用Linux的Shell命令都需要等待返回,所以常常我们设置的多线程都达不到效果,因此在调用shell命令不需要返回时,使用threading模块并不是最好的方法.   http: ...

  4. Appium+python自动化(三十六)- 士兵突击许三多 - 多个appium服务启动,多个设备启动,多进程并发启动设备-并发测试 - 上(超详解)

    简介 前面课程只是启动了单个appium服务,只能控制单台设备.如果需要针对多台设备测试那么该如何处理?而且发现群里的小伙伴们也在时不时地在讨论这个问题,想知道怎么实现的,于是宏哥就决定写一片这样的文 ...

  5. 【说解】在shell中通过mkfifo创建命名管道来控制多个进程并发执行

    背景: 工作中有两个异地机房需要传数据,数据全名很规范,在某个目录下命名为统一的前缀加上编号.如/path/from/file.{1..100}.而机房间的专线对单个scp进程的传输速度是有限制的,比 ...

  6. 正尝试在 OS 载入程序锁内执行托管代码。不要尝试在 DllMain 或映像初始化函数内执行托管代码,这样做会导致应用程序挂起。

    出错提示: 正尝试在 OS 载入程序锁内执行托管代码. 不要尝试在 DllMain 或映像初始化函数内执行托管代码,这样做会导致应用程序挂起. 原因分析: .NET2.0中添加了42种非常强大的调试助 ...

  7. 使用pabot并发执行robotframework的testSuite

    下载robotremoteserver-1.0.1.tar.gz.robotframework-pabot-0.22.tar.gz 执行以下命令,以安装pabot: pip install robot ...

  8. Python多进程并发(multiprocessing)用法实例详解

    http://www.jb51.net/article/67116.htm 本文实例讲述了Python多进程并发(multiprocessing)用法.分享给大家供大家参考.具体分析如下: 由于Pyt ...

  9. 多线程并发执行任务,取结果归集。终极总结:Future、FutureTask、CompletionService、CompletableFuture

    目录 1.Futrue 2.FutureTask 3.CompletionService 4.CompletableFuture 5.总结 ================正文分割线========= ...

随机推荐

  1. Java集合框架(set)

    set继承自collection接口,其子类和子接口如下: set的共同特性:不能添加相同的元素,通常无法记住元素添加的顺序 1.HashSet类 判断两元素相同的标准:1.equals方法返回tru ...

  2. Bootstrap 文件上传插件 FileInput的使用问题

    : 在使用bootstrap的文件上传插件fileinput http://plugins.krajee.com/file-input的预览功能时,删除预览图片在 bootstrap 模态框中没有用, ...

  3. NAIPC2018-K-Zoning Houses

    题目描述 Given a registry of all houses in your state or province, you would like to know the minimum si ...

  4. GridControl详解(九)表格中的控件

    选择完成控件后,可用+号点开ColumnEdit列,改控件的类型是RepositoryItem类型的,其相应的属性和相应的控件属性是类似的 构建数据如下: DataTable dt = new Dat ...

  5. (值类型引用类型)和null的关系

    1.null    null表示变量没有指向任何对象. 2.值类型    包括 bool.结构体.枚举.int.double.float等等 .在.NET中值类型都继承自ValueType. 3. 引 ...

  6. wepy 使用组件时一个注意事项。。。

    组件传值prop 必须使用指定引用地址的数据 如果像下面这样 取为空shop 中的phone ,后续获取数据或就算 shop中有phone元素 子组件也无法获取到修改后的值 <template& ...

  7. uboot之---make smdk2410_config命令详细解析

    先进入顶层Makefile.有很多相对不同板子的配置,如: gec2440_config:unconfig @$(MKCONFIG) $(@:_config=) arm arm920t gec2440 ...

  8. 数据库管理软件 Navicat Premium12 破解步骤

    数据库管理软件  Navicat Premium12B    https://pan.baidu.com/s/1QnAQwW-q0SQ1JglpFGxKOA   密码 : mwqc 里面的软件和补丁是 ...

  9. gunicorn之日志详细配置

    gunicorn的日志配置 gunicorn的日志配置相关的常用参数有4个,分别是accesslog,access_log_format,errorlog,loglevel. accesslog:用户 ...

  10. Ubuntu 上更新 Flash 插件

    2018-02-19 12:08:28 更新: 现在的 Google Chrome 浏览器自带了 Flash 支持,无需安装.而 Firefox 浏览器没有提供 Flash 支持,所以用 Firefo ...