PHP多進程實踐

來源:互聯網
上載者:User
PHP多進程實踐

1. 直接方式

pcntl_fork() 建立一個進程,在父進程傳回值是子進程的pid,在子進程傳回值是0,-1表示建立進程失敗。跟C非常相似。

測試指令碼 test.php

    // example of multiple processes    date_default_timezone_set( 'Asia/Chongqing');    echo "parent start, pid ", getmypid(), "\n" ;    beep();    for ($i=0; $i<3; ++$i){          $pid = pcntl_fork();           if ($pid == -1){                 die ("cannot fork" );          } else if ($pid > 0){                 echo "parent continue \n";                 for ($k=0; $k<2; ++$k){                      beep();                }          } else if ($pid == 0){                 echo "child start, pid ", getmypid(), "\n" ;                 for ($j=0; $j<5; ++$j){                      beep();                }                 exit ;          }    }    // ***    function beep(){           echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;          sleep(1);    }

用命令列運行
#php -f test.php

輸出結果
parent start, pid 1793
1793 2013-01-14 15:04:17
parent continue
1793 2013-01-14 15:04:18
child start, pid 1794
1794 2013-01-14 15:04:18
1794 2013-01-14 15:04:19
1793 2013-01-14 15:04:19
1794 2013-01-14 15:04:20
parent continue
1793 2013-01-14 15:04:20
child start, pid 1795
1795 2013-01-14 15:04:20
17931794 2013-01-14 15:04:212013-01-14 15:04:21

1795 2013-01-14 15:04:21
1794 2013-01-14 15:04:22
1795 2013-01-14 15:04:22
parent continue
1793 2013-01-14 15:04:22
child start, pid 1796
1796 2013-01-14 15:04:22
1793 2013-01-14 15:04:23
1796 2013-01-14 15:04:23
1795 2013-01-14 15:04:23
1795 2013-01-14 15:04:24
1796 2013-01-14 15:04:24
1796 2013-01-14 15:04:25
1796 2013-01-14 15:04:26

從中看到,建立了3個子進程,和父進程一起並行運行。其中有一行格式跟其他有些不同,
17931794 2013-01-14 15:04:212013-01-14 15:04:21
因為兩個進程同時進行寫操作,造成了衝突。

2. 阻塞方式

用直接方式,父進程建立了子進程後,並沒有等待子進程結束,而是繼續運行。似乎這裡看不到有什麼問題。如果php指令碼並不是運行完後自動結束,而是常駐記憶體的,就會造成子進程無法回收的問題。也就是殭屍進程。可以通過pcntl_wai()方法等待進程結束,然後回收已經結束的進程。
將測試指令碼改成:

$pid = pcntl_fork();if ($pid == -1){    ...} else if ($pid > 0){     echo "parent continue \n";     pcntl_wait($status);     for ($k=0; $k<2; ++$k){          beep();    }} else if ($pid == 0){     ...}

用命令列運行
#php -f test.php

輸出結果
parent start, pid 1807
1807 2013-01-14 15:20:05
parent continue
child start, pid 1808
1808 2013-01-14 15:20:06
1808 2013-01-14 15:20:07
1808 2013-01-14 15:20:08
1808 2013-01-14 15:20:09
1808 2013-01-14 15:20:10
1807 2013-01-14 15:20:11
1807 2013-01-14 15:20:12
parent continue
child start, pid 1809
1809 2013-01-14 15:20:13
1809 2013-01-14 15:20:14
1809 2013-01-14 15:20:15
1809 2013-01-14 15:20:16
1809 2013-01-14 15:20:17
1807 2013-01-14 15:20:18
1807 2013-01-14 15:20:19
child start, pid 1810
1810 2013-01-14 15:20:20
parent continue
1810 2013-01-14 15:20:21
1810 2013-01-14 15:20:22
1810 2013-01-14 15:20:23
1810 2013-01-14 15:20:24
1807 2013-01-14 15:20:25
1807 2013-01-14 15:20:26

父進程在pcntl_wait()將自己阻塞,等待子進程運行完了才接著運行。

3. 非阻塞方式

阻塞方式失去了多進程的並行性。還有一種方法,既可以回收已經結束的子進程,又可以並行。這就是非阻塞的方式。
修改指令碼:

    // example of multiple processes    date_default_timezone_set( 'Asia/Chongqing');    declare (ticks = 1);    pcntl_signal(SIGCHLD, "garbage" );    echo "parent start, pid ", getmypid(), "\n" ;    beep();    for ($i=0; $i<3; ++$i){          $pid = pcntl_fork();           if ($pid == -1){                 die ("cannot fork" );          } else if ($pid > 0){                 echo "parent continue \n";                 for ($k=0; $k<2; ++$k){                      beep();                }          } else if ($pid == 0){                 echo "child start, pid ", getmypid(), "\n" ;                 for ($j=0; $j<5; ++$j){                      beep();                }                 exit (0);          }    }    // parent    while (1){           // do something else          sleep(5);    }    // ***    function garbage($signal){           echo "signel $signal received\n" ;                     while (($pid = pcntl_waitpid(-1, $status, WNOHANG))> 0){                 echo "\t child end pid $pid , status $status\n" ;          }    }    function beep(){           echo getmypid(), "\t" , date( 'Y-m-d H:i:s', time()), "\n" ;          sleep(1);    }

用命令列運行
#php -f test.php &

輸出結果
parent start, pid 2066
2066 2013-01-14 16:45:34
parent continue
2066 2013-01-14 16:45:35
child start, pid 2067
2067 2013-01-14 16:45:35
20662067 2013-01-14 16:45:362013-01-14 16:45:36

2067 2013-01-14 16:45:37
parent continue
2066 2013-01-14 16:45:37
child start, pid 2068
2068 2013-01-14 16:45:37
2067 2013-01-14 16:45:38
2068 2013-01-14 16:45:38
2066 2013-01-14 16:45:38
parent continue
2066 2013-01-14 16:45:40
child start, pid 2069
2069 2067 2013-01-14 16:45:40
2013-01-14 16:45:40
2068 2013-01-14 16:45:40
2066 2013-01-14 16:45:41
2069 2013-01-14 16:45:41
2068 2013-01-14 16:45:41
signel 17 received
child end pid 2067, status 0
2069 2013-01-14 16:45:42
2068 2013-01-14 16:45:42
2069 2013-01-14 16:45:43
signel 17 received
child end pid 2068, status 0
2069 2013-01-14 16:45:44
signel 17 received
child end pid 2069, status 0

多個進程又並行運行了,而且運行大約10秒鐘之後,用 ps -ef | grep php 查看正在啟動並執行進程,只有一個進程
lqling 2066 1388 0 16:45 pts/1 00:00:00 php -f t5.php
是父進程,子進程被回收了。

4. 子進程退出狀態


pcntl_waitpid(-1, $status, WNOHANG) $status 返回子進程的結束狀態

5. windows多線程

windows系統不支援pcntl函數,幸好有curl_multi_exec()這個工具,利用內部的多線程,訪問多個連結,每個連結可以作為一個任務。

編寫指令碼 test1.php

    date_default_timezone_set( 'Asia/Chongqing');    $tasks = array(         'http://localhost/feedbowl/t2.php?job=task1',         'http://localhost/feedbowl/t2.php?job=task2',         'http://localhost/feedbowl/t2.php?job=task3'    );    $mh = curl_multi_init();    foreach ($tasks as $i => $task){         $ch[$i] = curl_init();         curl_setopt($ch[$i], CURLOPT_URL, $task);         curl_setopt($ch[$i], CURLOPT_RETURNTRANSFER, 1);         curl_multi_add_handle($mh, $ch[$i]);    }    do {$mrc = curl_multi_exec($mh,$active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);    while ($active && $mrc == CURLM_OK) {         if (curl_multi_select($mh) != -1) {           do {$mrc = curl_multi_exec($mh, $active); } while ($mrc == CURLM_CALL_MULTI_PERFORM);         }    }    // completed, checkout result    foreach ($tasks as $j => $task){         if (curl_error($ch[$j])){              echo "task ${j} [$task ] error " , curl_error($ch[$j]), "\r\n" ;         } else {              echo "task ${j} [$task ] get: \r\n" , curl_multi_getcontent($ch[$j]), "\r\n" ;         }    }

編寫指令碼 test2.php

    date_default_timezone_set( 'Asia/Chongqing');    echo "child start, pid ", getmypid(), "\r\n" ;    for ($i=0; $i<5; ++$i){         beep();    }    exit (0);    // ***    function beep(){        echo getmypid(), "\t" , date('Y-m-d H:i:s' , time()), "\r\n";        sleep(1);    }

用命令列運行
#php -f test1.php &

輸出結果
task 0 [http://localhost/feedbowl/t2.php?job=task1] get:
child start, pid 5804
5804 2013-01-15 20:22:35
5804 2013-01-15 20:22:36
5804 2013-01-15 20:22:37
5804 2013-01-15 20:22:38
5804 2013-01-15 20:22:39

task 1 [http://localhost/feedbowl/t2.php?job=task2] get:
child start, pid 5804
5804 2013-01-15 20:22:35
5804 2013-01-15 20:22:36
5804 2013-01-15 20:22:37
5804 2013-01-15 20:22:38
5804 2013-01-15 20:22:39

task 2 [http://localhost/feedbowl/t2.php?job=task3] get:
child start, pid 5804
5804 2013-01-15 20:22:35
5804 2013-01-15 20:22:36
5804 2013-01-15 20:22:37
5804 2013-01-15 20:22:38
5804 2013-01-15 20:22:39

從列印的時間看到,多個任務幾乎是同時啟動並執行。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.