免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 2369 | 回复: 1
打印 上一主题 下一主题

PHP中的(伪)多线程与多进程 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2011-11-09 11:29 |只看该作者 |倒序浏览
本帖最后由 中关村村草 于 2011-11-09 11:30 编辑

PHP中的(伪)多线程与多进程




已经因为没怎么需要,所以没有查这个的资料。最近有一个项目却是需要这样子的功能。

查看了PHP的手册和他人的例子,了解到基本的两种方法:

(伪)多线程:借助外力
利用WEB服务器本身的多线程来处理,从WEB服务器多次调用我们需要实现多线程的程序。
以下转载自:http://www.laikan8.com/21/118472.html


QUOTE:
我们知道PHP本身是不支持多线程的, 但是我们的WEB服务器是支持多线程的.

也就是说可以同时让多人一起访问. 这也是我在PHP中实现多线程的基础.

假设我们现在运行的是a.php这个文件. 但是我在程序中又请求WEB服务器运行另一个b.php

那么这两个文件将是同时执行的.

(PS: 一个链接请求发送之后, WEB服务器就会执行它, 而不管客户端是否已经退出)

有些时候, 我们想运行的不是另一个文件, 而是本文件中的一部分代码.该怎么办呢?

其实可是通过参数来控制a.php来运行哪一段程序.

下面看一个例子:

//a.php

Python代码
  1. 1.<?php   
  2. 2.function runThread()   
  3. 3.{   
  4. 4.       $fp = fsockopen('localhost', 80, $errno, $errmsg);     
  5. 5.       fputs($fp, "GET /a.php?act=brnrn");        //这里的第二个参数是HTTP协议中规定的请求头     
  6. 6.                               //不明白的请看RFC中的定义   
  7. 7.       fclose($fp);   
  8. 8.}   
  9. 9.  
  10. 10.function a()   
  11. 11.{   
  12. 12.       $fp = fopen('result_a.log', 'w');     
  13. 13.       fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");     
  14. 14.       fclose($fp);         
  15. 15.}   
  16. 16.  
  17. 17.function b()   
  18. 18.{   
  19. 19.       $fp = fopen('result_b.log', 'w');     
  20. 20.       fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");     
  21. 21.       fclose($fp);         
  22. 22.}   
  23. 23.if(!isset($_GET['act'])) $_GET['act'] = 'a';     
  24. 24.if($_GET['act'] == 'a')     
  25. 25.{   
  26. 26.       runThread();   
  27. 27.       a();   
  28. 28.}   
  29. 29.else if($_GET['act'] == 'b') b();     
  30. 30.?>   
复制代码
Python代码
  1. 1.<?php   
  2. 2.function runThread()   
  3. 3.{   
  4. 4.       $fp = fsockopen('localhost', 80, $errno, $errmsg);   
  5. 5.       fputs($fp, "GET /a.php?act=brnrn");        //这里的第二个参数是HTTP协议中规定的请求头   
  6. 6.                               //不明白的请看RFC中的定义   
  7. 7.       fclose($fp);   
  8. 8.}   
  9. 9.  
  10. 10.function a()   
  11. 11.{   
  12. 12.       $fp = fopen('result_a.log', 'w');   
  13. 13.       fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");   
  14. 14.       fclose($fp);         
  15. 15.}   
  16. 16.  
  17. 17.function b()   
  18. 18.{   
  19. 19.       $fp = fopen('result_b.log', 'w');   
  20. 20.       fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");   
  21. 21.       fclose($fp);         
  22. 22.}   
  23. 23.if(!isset($_GET['act'])) $_GET['act'] = 'a';   
  24. 24.if($_GET['act'] == 'a')   
  25. 25.{   
  26. 26.       runThread();   
  27. 27.       a();   
  28. 28.}   
  29. 29.else if($_GET['act'] == 'b') b();   
  30. 30.?>   
  31. <?php
  32. function runThread()
  33. {
  34.        $fp = fsockopen('localhost', 80, $errno, $errmsg);
  35.        fputs($fp, "GET /a.php?act=brnrn");        //这里的第二个参数是HTTP协议中规定的请求头
  36.                                //不明白的请看RFC中的定义
  37.        fclose($fp);
  38. }

  39. function a()
  40. {
  41.        $fp = fopen('result_a.log', 'w');
  42.        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
  43.        fclose($fp);      
  44. }

  45. function b()
  46. {
  47.        $fp = fopen('result_b.log', 'w');
  48.        fputs($fp, 'Set in ' . Date('h:i:s', time()) . (double)microtime() . "rn");
  49.        fclose($fp);      
  50. }
  51. if(!isset($_GET['act'])) $_GET['act'] = 'a';
  52. if($_GET['act'] == 'a')
  53. {
  54.        runThread();
  55.        a();
  56. }
  57. else if($_GET['act'] == 'b') b();
  58. ?>
复制代码
打开result_a.log 和 result_b.log 比较一下两个文件的中访问的时间. 大家会发现, 这两个的确是在不同线程中运行的.
有些时间完全一样.

上面只是一个简单的例子, 大家可以改进成其它形式.


既然PHP中也能多线程了, 那么问题也来了, 那就是同步的问题. 我们知道 PHP本身是不支持多线程的. 所以更不会有什么像

Java 中synchronize的方法了. 那我们该如何做呢.

1. 尽量不访问同一个资源. 以避免冲突. 但是可以同时像数据库操作. 因为数据库是支持并发操作的. 所以在多线程的PHP中

不要向同一个文件中写入数据. 如果必须要写的话, 用别的方法进行同步.. 如调用 flock对文件进行加锁等. 或建立临时文件

并在另外的线程中等待这个文件的消失 while(file_exits('xxx')); 这样就等于这个临时文件存在时, 表示其实线程正在操作

如果没有了这个文件, 说明其它线程已经释放了这个.

2. 尽量不要从runThread在执行fputs后取这个socket中读取数据. 因为要实现多线程, 需要的用非阻塞模式. 即在像fgets这

样的函数时立即返回.. 所以读写数据就会出问题. 如果使用阻塞模式的话, 程序就不算是多线程了. 他要等上面的返回才执行

下面的程序. 所以如果需要交换数据最后利用外面文件或数据中完成. 实在想要的话就用socket_set_nonblock($fp) 来实现.


说了这么多, 倒底这个有没有实际的意义呢? 在什么时候需要这种用这种方法呢 ?

答案是肯定的. 大家知道. 在一个不断读取网络资源的应用中, 网络的速度是瓶颈. 如果采多这种形式就可以同时以多个线程对

不同的页面进行读取.

本人做的一个能从8848、soaso这些商城网站搜索信息的程序。还有一个从阿里巴巴网站上读取商业信息和公司目录的程序,批量抢注CN域名程序也用到了此技术。 因为这两个程序都是要不断的链接它们的服务器读取信息并保存到数据库。 利用此技术正好消除了在等待响应时的瓶

颈。


多进程:使用PHP的Process Control Functions(PCNTL/线程控制函数)
函数参考可见:http://www.php.net/manual/zh/ref.pcntl.php
只能用在Unix Like OS,Windows不可用。
编译php的时候,需要加上--enable-pcntl,且推荐仅仅在CLI模式运行,不要在WEB服务器环境运行。

CLI模式(也就是命令行)。
CLI的全称是Command-Line Interface


以下为简短的测试代码:

Python代码
  1. 1.<?php   
  2. 2.declare(ticks=1);     
  3. 3.$bWaitFlag = FALSE; /// 是否等待进程结束   
  4. 4.$intNum = 10;           /// 进程总数     
  5. 5.$pids = array();        ///   进程PID数组   
  6. 6.  
  7. 7.echo ("Start\n");     
  8. 8.  
  9. 9.for($i = 0; $i < $intNum; $i++) {     
  10. 10.  
  11. 11.   $pids[$i] = pcntl_fork();/// 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息   
  12. 12.  
  13. 13.   if(!$pids[$i]) {     
  14. 14.// 子进程进程代码段_Start   
  15. 15.$str="";   
  16. 16.sleep(5+$i);     
  17. 17.for ($j=0;$j<$i;$j++) {$str.="*";}     
  18. 18.echo "$i -> " . time() . " $str \n";     
  19. 19.exit();   
  20. 20.// 子进程进程代码段_End   
  21. 21.   }   
  22. 22.  
  23. 23.}   
  24. 24.if ($bWaitFlag)     
  25. 25.{   
  26. 26.   for($i = 0; $i < $intNum; $i++) {     
  27. 27.pcntl_waitpid($pids[$i], $status, WUNTRACED);   
  28. 28.echo "wait $i -> " . time() . "\n";     
  29. 29.   }   
  30. 30.}   
  31. 31.echo ("End\n");     
  32. 32.?>   
  33. 33.  
  34. 34.  
复制代码
35.运行结果如下:   
Python代码
  1. 1.<?php   
  2. 2.declare(ticks=1);   
  3. 3.$bWaitFlag = FALSE; /// 是否等待进程结束   
  4. 4.$intNum = 10;           /// 进程总数   
  5. 5.$pids = array();        ///   进程PID数组   
  6. 6.  
  7. 7.echo ("Start\n");   
  8. 8.  
  9. 9.for($i = 0; $i < $intNum; $i++) {   
  10. 10.  
  11. 11.   $pids[$i] = pcntl_fork();/// 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息   
  12. 12.  
  13. 13.   if(!$pids[$i]) {   
  14. 14.// 子进程进程代码段_Start   
  15. 15.$str="";   
  16. 16.sleep(5+$i);   
  17. 17.for ($j=0;$j<$i;$j++) {$str.="*";}   
  18. 18.echo "$i -> " . time() . " $str \n";   
  19. 19.exit();   
  20. 20.// 子进程进程代码段_End   
  21. 21.   }   
  22. 22.  
  23. 23.}   
  24. 24.if ($bWaitFlag)   
  25. 25.{   
  26. 26.   for($i = 0; $i < $intNum; $i++) {   
  27. 27.pcntl_waitpid($pids[$i], $status, WUNTRACED);   
  28. 28.echo "wait $i -> " . time() . "\n";   
  29. 29.   }   
  30. 30.}   
  31. 31.echo ("End\n");   
  32. 32.?>   
  33. 33.  
  34. 34.  
  35. 35.运行结果如下:   
  36. <?php
  37. declare(ticks=1);
  38. $bWaitFlag = FALSE; /// 是否等待进程结束
  39. $intNum = 10;           /// 进程总数
  40. $pids = array();        ///   进程PID数组

  41. echo ("Start\n");

  42. for($i = 0; $i < $intNum; $i++) {

  43.    $pids[$i] = pcntl_fork();/// 产生子进程,而且从当前行之下开试运行代码,而且不继承父进程的数据信息

  44.    if(!$pids[$i]) {
  45. // 子进程进程代码段_Start
  46. $str="";
  47. sleep(5+$i);
  48. for ($j=0;$j<$i;$j++) {$str.="*";}
  49. echo "$i -> " . time() . " $str \n";
  50. exit();
  51. // 子进程进程代码段_End
  52.    }

  53. }
  54. if ($bWaitFlag)
  55. {
  56.    for($i = 0; $i < $intNum; $i++) {
  57. pcntl_waitpid($pids[$i], $status, WUNTRACED);
  58. echo "wait $i -> " . time() . "\n";
  59.    }
  60. }
  61. echo ("End\n");
  62. ?>
复制代码
运行结果如下:

CODE:

Java代码
  1. 1.[qiao@oicq qiao]$ php test.php         
  2. 2.Start   
  3. 3.End   
  4. 4.[qiao@oicq qiao]$ ps -aux | grep "php"     
  5. 5.qiao     32275   0.0   0.5 49668 6148 pts/1 S 14:03 0:00 /usr/local/php4/b     
  6. 6.qiao     32276   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  7. 7.qiao     32277   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  8. 8.qiao     32278   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  9. 9.qiao     32279   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  10. 10.qiao     32280   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  11. 11.qiao     32281   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  12. 12.qiao     32282   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  13. 13.qiao     32283   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  14. 14.qiao     32284   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b     
  15. 15.qiao     32286   0.0   0.0   1620   600 pts/1 S 14:03 0:00 grep php     
  16. 16.[qiao@oicq qiao]$ 0 -> 1133503401      
  17. 17.1 -> 1133503402 *     
  18. 18.2 -> 1133503403 **     
  19. 19.3 -> 1133503404 ***     
  20. 20.4 -> 1133503405 ****     
  21. 21.5 -> 1133503406 *****     
  22. 22.6 -> 1133503407 ******     
  23. 23.7 -> 1133503408 *******     
  24. 24.8 -> 1133503409 ********     
  25. 25.9 -> 1133503410 *********     
  26. 26.  
  27. 27.[qiao@oicq qiao]$     
  28. 28.  
  29. 29.  
  30. 30.如果$bWaitFlag=TURE,则结果如下:   
  31. 31.  
  32. 32.  
  33. 33.  
  34. 34.[Copy to clipboard] [ - ]   
  35. 35.CODE:   
  36. 36.[qiao@oicq qiao]$ php test.php           
  37. 37.Start   
  38. 38.0 -> 1133503602      
  39. 39.wait 0 -> 1133503602     
  40. 40.1 -> 1133503603 *     
  41. 41.wait 1 -> 1133503603     
  42. 42.2 -> 1133503604 **     
  43. 43.wait 2 -> 1133503604     
  44. 44.3 -> 1133503605 ***     
  45. 45.wait 3 -> 1133503605     
  46. 46.4 -> 1133503606 ****     
  47. 47.wait 4 -> 1133503606     
  48. 48.5 -> 1133503607 *****     
  49. 49.wait 5 -> 1133503607     
  50. 50.6 -> 1133503608 ******     
  51. 51.wait 6 -> 1133503608     
  52. 52.7 -> 1133503609 *******     
  53. 53.wait 7 -> 1133503609     
  54. 54.8 -> 1133503610 ********     
  55. 55.wait 8 -> 1133503610     
  56. 56.9 -> 1133503611 *********     
  57. 57.wait 9 -> 1133503611     
  58. 58.End   
  59. 59.[qiao@oicq qiao]$   
  60. Java代码  
  61. 1.[qiao@oicq qiao]$ php test.php         
  62. 2.Start   
  63. 3.End   
  64. 4.[qiao@oicq qiao]$ ps -aux | grep "php"   
  65. 5.qiao     32275   0.0   0.5 49668 6148 pts/1 S 14:03 0:00 /usr/local/php4/b   
  66. 6.qiao     32276   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  67. 7.qiao     32277   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  68. 8.qiao     32278   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  69. 9.qiao     32279   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  70. 10.qiao     32280   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  71. 11.qiao     32281   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  72. 12.qiao     32282   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  73. 13.qiao     32283   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  74. 14.qiao     32284   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b   
  75. 15.qiao     32286   0.0   0.0   1620   600 pts/1 S 14:03 0:00 grep php   
  76. 16.[qiao@oicq qiao]$ 0 -> 1133503401     
  77. 17.1 -> 1133503402 *   
  78. 18.2 -> 1133503403 **   
  79. 19.3 -> 1133503404 ***   
  80. 20.4 -> 1133503405 ****   
  81. 21.5 -> 1133503406 *****   
  82. 22.6 -> 1133503407 ******   
  83. 23.7 -> 1133503408 *******   
  84. 24.8 -> 1133503409 ********   
  85. 25.9 -> 1133503410 *********   
  86. 26.  
  87. 27.[qiao@oicq qiao]$   
  88. 28.  
  89. 29.  
  90. 30.如果$bWaitFlag=TURE,则结果如下:   
  91. 31.  
  92. 32.  
  93. 33.  
  94. 34.[Copy to clipboard] [ - ]   
  95. 35.CODE:   
  96. 36.[qiao@oicq qiao]$ php test.php         
  97. 37.Start   
  98. 38.0 -> 1133503602     
  99. 39.wait 0 -> 1133503602   
  100. 40.1 -> 1133503603 *   
  101. 41.wait 1 -> 1133503603   
  102. 42.2 -> 1133503604 **   
  103. 43.wait 2 -> 1133503604   
  104. 44.3 -> 1133503605 ***   
  105. 45.wait 3 -> 1133503605   
  106. 46.4 -> 1133503606 ****   
  107. 47.wait 4 -> 1133503606   
  108. 48.5 -> 1133503607 *****   
  109. 49.wait 5 -> 1133503607   
  110. 50.6 -> 1133503608 ******   
  111. 51.wait 6 -> 1133503608   
  112. 52.7 -> 1133503609 *******   
  113. 53.wait 7 -> 1133503609   
  114. 54.8 -> 1133503610 ********   
  115. 55.wait 8 -> 1133503610   
  116. 56.9 -> 1133503611 *********   
  117. 57.wait 9 -> 1133503611   
  118. 58.End   
  119. 59.[qiao@oicq qiao]$   
  120. [qiao@oicq qiao]$ php test.php      
  121. Start
  122. End
  123. [qiao@oicq qiao]$ ps -aux | grep "php"
  124. qiao     32275   0.0   0.5 49668 6148 pts/1 S 14:03 0:00 /usr/local/php4/b
  125. qiao     32276   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  126. qiao     32277   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  127. qiao     32278   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  128. qiao     32279   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  129. qiao     32280   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  130. qiao     32281   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  131. qiao     32282   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  132. qiao     32283   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  133. qiao     32284   0.0   0.5 49668 6152 pts/1 S 14:03 0:00 /usr/local/php4/b
  134. qiao     32286   0.0   0.0   1620   600 pts/1 S 14:03 0:00 grep php
  135. [qiao@oicq qiao]$ 0 -> 1133503401  
  136. 1 -> 1133503402 *
  137. 2 -> 1133503403 **
  138. 3 -> 1133503404 ***
  139. 4 -> 1133503405 ****
  140. 5 -> 1133503406 *****
  141. 6 -> 1133503407 ******
  142. 7 -> 1133503408 *******
  143. 8 -> 1133503409 ********
  144. 9 -> 1133503410 *********

  145. [qiao@oicq qiao]$
复制代码
如果$bWaitFlag=TURE,则结果如下:
  1. [Copy to clipboard] [ - ]
  2. CODE:
  3. [qiao@oicq qiao]$ php test.php      
  4. Start
  5. 0 -> 1133503602  
  6. wait 0 -> 1133503602
  7. 1 -> 1133503603 *
  8. wait 1 -> 1133503603
  9. 2 -> 1133503604 **
  10. wait 2 -> 1133503604
  11. 3 -> 1133503605 ***
  12. wait 3 -> 1133503605
  13. 4 -> 1133503606 ****
  14. wait 4 -> 1133503606
  15. 5 -> 1133503607 *****
  16. wait 5 -> 1133503607
  17. 6 -> 1133503608 ******
  18. wait 6 -> 1133503608
  19. 7 -> 1133503609 *******
  20. wait 7 -> 1133503609
  21. 8 -> 1133503610 ********
  22. wait 8 -> 1133503610
  23. 9 -> 1133503611 *********
  24. wait 9 -> 1133503611
  25. End
  26. [qiao@oicq qiao]$
复制代码
从多进程的例子可以看出,使用pcntl_fork()之后,将生成一个子进程,而且子进程运行的代码,从pcntl_fork()之后的代码开始,而子进程不继承父进程的数据信息(实际上是把父进程的数据做了一个全新的拷贝),因而使用if(!$pids[$i]) 来控制子进程实际运行的代码段。

论坛徽章:
0
2 [报告]
发表于 2011-11-14 09:38 |只看该作者
希望与ll能多交流
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP