} } }

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

    添加时间:2013-5-17 点击量:

    (伪)多线程:借助外力


        哄骗WEB办事器本身的多线程来处理惩罚,从WEB办事器多次调用我们须要实现多线程的法度。




    QUOTE:


    我们知道PHP本身是不支撑多线程的, 然则我们的WEB办事器是支撑多线程的.


    也就是说可以同时让多人一路接见. 这也是我在PHP中实现多线程的根蒂根基.


    假设我们如今运行的是a.php这个文件. 然则我在法度中又恳求WEB办事器运行另一个b.php


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


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


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


    其实可是经由过程参数来把握a.php来运行哪一段法度.


    下面看一个例子:




    [php] view plaincopy





    1. <?php  

    2. function runThread(){  

    3.     ¥fp = fsockopenlocalhost, 80, ¥errno, ¥errmsg);  

    4.     fputs¥fp, GET /a.php?act=brnrn);//这里的第二个参数是HTTP和谈中规定的恳求头,不熟悉打听的请看RFC中的定义                               

    5.     fclose(¥fp);  

    6. }  

    7. function a(){  

    8.     ¥fp = fopenresult_a.log, w);  

    9.     fputs¥fp, Set in  . Dateh:i:s, time()) . (double)microtime() . rn);  

    10.     fclose(¥fp);          

    11. }  

    12. function b(){  

    13.     ¥fp = fopenresult_b.log, w);  

    14.     fputs¥fp, Set in  . Dateh:i:s, time()) . (double)microtime() . rn);  

    15.     fclose(¥fp);          

    16. }  

    17. if(!isset(¥_GET[act])){ ¥_GET[act] = a;};  

    18.   

    19. if¥_GET[act] == a){  

    20.     runThread();  

    21.     a();  

    22. }else if¥_GET[act] == b){   

    23.     b();  

    24. };  

    25. ?>  





    打开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这些商城网站搜刮信息的法度。还有一个从阿里巴巴网站上读取贸易信息和公司目次的法度也用到


    了此技巧。 因为这两个法度都是要络续的链接它们的办事器读取信息并保存到数据库。 哄骗此技巧正好打消了在守候响应时的瓶


    颈。

    多过程:应用PHP的Process Control Functions(PCNTL/线程把握函数)

        函数参考可见:http://www.php.net/manual/zh/ref.pcntl.php

        只能用在Unix Like OS,Windows不成用。

        编译php的时辰,须要加上--enable-pcntl,且推荐仅仅在CLI模式运行,不要在WEB办事器景象运行。

        以下为简短的测试代码:




    [php] view plaincopy





    1. declare(ticks=1);  

    2. ¥bWaitFlag = FALSE; /// 是否守候过程停止  

    3. ¥intNum = 10;           /// 过程总数  

    4. ¥pids = array();        ///  过程PID数组  

    5. echo (Start\n);  

    6. for¥i = 0; ¥i < ¥intNum¥i++) {  

    7.   ¥pids[¥i] = pcntl_fork();/// 产生子过程,并且从当前行之下开试运行代码,并且不持续父过程的数据信息  

    8.   if(!¥pids[¥i]) {  

    9.     // 子过程过程代码段_Start  

    10.     ¥str=;  

    11.     sleep(5+¥i);  

    12.     for (¥j=0;¥j<¥i;¥j++) {¥str.=;}  

    13.     echo ¥i ->  . time() .  ¥str \n;  

    14.     exit();  

    15.     // 子过程过程代码段_End  

    16.   }  

    17. }  

    18. if (¥bWaitFlag)  

    19. {  

    20.   for¥i = 0; ¥i < ¥intNum¥i++) {  

    21.     pcntl_waitpid(¥pids[¥i], ¥status, WUNTRACED);  

    22.     echo wait ¥i ->  . time() . \n;  

    23.   }  

    24. }  

    25. echo (End\n);  





    运行成果如下:


    CODE:[Copy toclipboard][qiao@oicq qiao]¥ phptest.php       

    Start

    End

    [qiao@oicq qiao]¥ ps -aux | grep php

    qiao     32275  0.0  0.5 49668 6148pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32276  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32277  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32278  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32279  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32280  0.0  0.5 49668 6152pts/1    S    14:03   0:00 /usr/local/php4/b

    qiao     32281  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32282  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32283  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32284  0.0  0.5 49668 6152pts/1    S    14:03   0:00/usr/local/php4/b

    qiao     32286  0.0  0.0  1620  600pts/1    S    14:03   0:00 grep php

    [qiao@oicq qiao]¥ 0 -> 1133503401 

    1 -> 1133503402

    2 -> 1133503403

    3 -> 1133503404

    4 -> 1133503405

    5 -> 1133503406

    6 -> 1133503407

    7 -> 1133503408

    8 -> 1133503409

    9 -> 1133503410


    [qiao@oicq qiao]¥

    若是¥bWaitFlag=TURE,则成果如下:


    CODE:[Copy toclipboard][qiao@oicq qiao]¥ phptest.php       

    Start

    0 -> 1133503602 

    wait 0 -> 1133503602

    1 -> 1133503603

    wait 1 -> 1133503603

    2 -> 1133503604

    wait 2 -> 1133503604

    3 -> 1133503605

    wait 3 -> 1133503605

    4 -> 1133503606

    wait 4 -> 1133503606

    5 -> 1133503607

    wait 5 -> 1133503607

    6 -> 1133503608

    wait 6 -> 1133503608

    7 -> 1133503609

    wait 7 -> 1133503609

    8 -> 1133503610

    wait 8 -> 1133503610

    9 -> 1133503611

    wait 9 -> 1133503611

    End

    [qiao@oicq qiao]¥


    多过程的例子可以看出,应用pcntl_fork()之后,将生成一个子过程,并且子过程运行的代码,从pcntl_fork()之后的代码开端,而子进
    程不持续父过程的数据信息(实际上是把父过程的数据做了一个全新的拷贝),因而应用if(!¥pids[¥i]) 来把握子过程实际运行的代码段。


        更具体的研究出于时候关系,临时没有进行,你可以参考我给出的手册的链接。




    [文章二] 测验测验php号令行脚本多过程并发履行


    作者:dulao5

    起原:http://dulao5.blog.hexun.com/3726837_d.html


    除了fork, cli下的并发体式格式还有一种,看我的例子:




    php不支撑多线程,然则我们可以把题目转换成“多过程”来解决。因为php中的pcntl_fork只有unix平台才可以应用,所以本文测验测验应用popen来调换。




    下面是一个例子:


    被并行调用的子法度代码:




    [php] view plaincopy





    1. <?php  

    2. if¥argc==1){  

    3.     echoargv\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.         sleep(1);  

    11.         echo¥i..2..time(). exec ¥arg \n);  

    12.         sleep(1);  

    13.     }else{  

    14.         sleep(1);  

    15.     }  

    16. }  

    17. ?>  





    主调用者法度,由他调用子过程,同时并发的收集子法度的输出




    [php] view plaincopy





    1. error_reporting(E_ALL);  

    2.   

    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.   

    7. echo ¥handle1;  . gettype¥handle1) . \n;  

    8. echo ¥handle2;  . gettype¥handle2) . \n;  

    9. echo ¥handle3;  . gettype¥handle3) . \n;  

    10. //sleep(20);  

    11. while(!feof¥handle1) || !feof¥handle2) || !feof¥handle3) )  

    12. {  

    13. ¥read = fgets¥handle1);  

    14. echo ¥read;  

    15. ¥read = fgets¥handle2);  

    16. echo ¥read;  

    17. ¥read = fgets¥handle3);  

    18. echo ¥read;  

    19. }  

    20. pclose(¥handle1);  

    21. pclose(¥handle2);  

    22. pclose(¥handle3);  





    下面是我机械上的输出:


    C:\my_hunter>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任务的调剂者,本法度读取一个任务文件,把里面的每行号令并发履行, 可以设置同时存在的子过程数量:




    [php] view plaincopy





    1. / 

    2.    主任务经管器 

    3.    并发的履行子任务列表 

    4. /  

    5. include../common/conf.php);  

    6. include../common/function.php);  

    7. //开启的过程数  

    8. ¥exec_number = 40 ;  

    9. / main /  

    10. if¥argc==1){  

    11.     echoargv\n);  

    12. }  

    13. ¥taskfile = ¥argv[1];  

    14. //tasklist  

    15. ¥tasklist = file(¥taskfile);  

    16. ¥tasklist_len = count¥tasklist);  

    17. ¥tasklist_pos = 0;  

    18. ¥handle_list = array();  

    19. while(1)  

    20. {  

    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.         iffeof¥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. }  

    62. tolog(\n\nend\n\n,  ,  true);  





    附加一段Socket多过程接管的代码:




    [php] view plaincopy





    1. do {  

    2.  if ((¥msgsock = socket_accept(¥sock)) < 0) {  

    3.   echo socket_accept() failed: reason:  . socket_strerror(¥msgsock) . \n;  

    4.   break;  

    5.  }  

    6.  ¥pid = pcntl_fork();  

    7.  if (¥pid == -1) {  

    8.   diecould not fork);  

    9.  } else if (!¥pid) {  

    10.   .....  

    11.   socket_write(¥msgsock, ¥msg, strlen¥msg));  

    12.   do {  

    13.    ......  

    14.   } while (true);  

    15.    socket_close(¥msgsock);  

    16.  }  

    17. while (true);  







    原文地址:http://www.alixixi.com/program/a/2008050731686.shtml

    彼此相爱,却不要让爱成了束缚:不如让它成为涌动的大海,两岸乃是你们的灵魂。互斟满杯,却不要同饮一杯。相赠面包,却不要共食一个。一起歌舞欢喜,却依然各自独立,相互交心,却不是让对方收藏。因为唯有生命之手,方能收容你们的心。站在一起却不要过于靠近。—— 纪伯伦《先知》
    分享到: