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. time_t与GMT格式互转

    time_t Time::timeFromGMT(string gmt) { char week[4]; memset(week,0,4); char month[4]; memset(month,0 ...

  2. Windows API函数大全(精心总结)

    WindowsAPI函数大全(精心总结)    目录 1. API之网络函数... 1 2. API之消息函数... 1 3. API之文件处理函数... 2 4. API之打印函数... 5 5. ...

  3. HDU 4946 凸包

    给你n个点,具有速度,一个位置如果有其他点能够先到,则不能继续访问,求出里面这些点哪些点是能够无限移动的. 首先我们考虑到,一个速度小的和一个速度大的,速度小的必定只有固定他周围的一定区域是它先到的, ...

  4. Bargaining Table

    Bargaining Table time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...

  5. Jdbc练习

    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import ...

  6. 原生js写的一个简单slider

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  7. 洛谷 1.5.1 Number Triangles 数字金字塔

    Description 考虑在下面被显示的数字金字塔. 写一个程序来计算从最高点开始在底部任意处结束的路径经过数字的和的最大. 每一步可以走到左下方的点也可以到达右下方的点. 7 3 8 8 1 0 ...

  8. SMB MS17-010 利用(CVE-2017-0144 )

    exploit-db : https://www.exploit-db.com/exploits/42315/ 该漏洞的影响版本很广泛:Microsoft Windows Windows 7/8.1/ ...

  9. Sublime text 3中文汉化教程

    想弄个中文版的sublime,居然可以不用重新下载汉化包或者重新下载简体中文版了~而是只需要安装个插件即可!   工具/原料   电脑 sublime text3编辑器 方法/步骤    启动并进入s ...

  10. CentOS 6.5 安装 MongoDB

    1. 配置 yum 新建 /etc/yum.repos.d/mongodb-org-3.4.repo 文件,使用以下配置:(适用于 MongoDB 3.0 以后版本) [mongodb-org-3.4 ...