目錄
1. 直接方式
2. 阻塞方式
3. 非阻塞方式
4. 子进程退出状态
5. windows多线程
首頁 後端開發 php教程 PHP多进程实践

PHP多进程实践

Jun 20, 2016 pm 01:03 PM
php 多線程

​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( &#39;Y-m-d H:i:s&#39;, 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( &#39;Asia/Chongqing&#39;);
    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( &#39;Y-m-d H:i:s&#39;, 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( &#39;Asia/Chongqing&#39;);
    $tasks = array(
         &#39;http://localhost/feedbowl/t2.php?job=task1&#39;,
         &#39;http://localhost/feedbowl/t2.php?job=task2&#39;,
         &#39;http://localhost/feedbowl/t2.php?job=task3&#39;
    );
    $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( &#39;Asia/Chongqing&#39;);
    echo "child start, pid ", getmypid(), "\r\n" ;
    for ($i=0; $i<5; ++$i){
         beep();
    }
    exit (0);
    // ***
    function beep(){
        echo getmypid(), "\t" , date(&#39;Y-m-d H:i:s&#39; , 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

从打印的时间看到,多个任务几乎是同时运行的。


本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

在PHP API中說明JSON Web令牌(JWT)及其用例。 在PHP API中說明JSON Web令牌(JWT)及其用例。 Apr 05, 2025 am 12:04 AM

JWT是一種基於JSON的開放標準,用於在各方之間安全地傳輸信息,主要用於身份驗證和信息交換。 1.JWT由Header、Payload和Signature三部分組成。 2.JWT的工作原理包括生成JWT、驗證JWT和解析Payload三個步驟。 3.在PHP中使用JWT進行身份驗證時,可以生成和驗證JWT,並在高級用法中包含用戶角色和權限信息。 4.常見錯誤包括簽名驗證失敗、令牌過期和Payload過大,調試技巧包括使用調試工具和日誌記錄。 5.性能優化和最佳實踐包括使用合適的簽名算法、合理設置有效期、

會話如何劫持工作,如何在PHP中減輕它? 會話如何劫持工作,如何在PHP中減輕它? Apr 06, 2025 am 12:02 AM

會話劫持可以通過以下步驟實現:1.獲取會話ID,2.使用會話ID,3.保持會話活躍。在PHP中防範會話劫持的方法包括:1.使用session_regenerate_id()函數重新生成會話ID,2.通過數據庫存儲會話數據,3.確保所有會話數據通過HTTPS傳輸。

描述紮實的原則及其如何應用於PHP的開發。 描述紮實的原則及其如何應用於PHP的開發。 Apr 03, 2025 am 12:04 AM

SOLID原則在PHP開發中的應用包括:1.單一職責原則(SRP):每個類只負責一個功能。 2.開閉原則(OCP):通過擴展而非修改實現變化。 3.里氏替換原則(LSP):子類可替換基類而不影響程序正確性。 4.接口隔離原則(ISP):使用細粒度接口避免依賴不使用的方法。 5.依賴倒置原則(DIP):高低層次模塊都依賴於抽象,通過依賴注入實現。

在PHPStorm中如何進行CLI模式的調試? 在PHPStorm中如何進行CLI模式的調試? Apr 01, 2025 pm 02:57 PM

在PHPStorm中如何進行CLI模式的調試?在使用PHPStorm進行開發時,有時我們需要在命令行界面(CLI)模式下調試PHP�...

PHP 8.1中的枚舉(枚舉)是什麼? PHP 8.1中的枚舉(枚舉)是什麼? Apr 03, 2025 am 12:05 AM

PHP8.1中的枚舉功能通過定義命名常量增強了代碼的清晰度和類型安全性。 1)枚舉可以是整數、字符串或對象,提高了代碼可讀性和類型安全性。 2)枚舉基於類,支持面向對象特性,如遍歷和反射。 3)枚舉可用於比較和賦值,確保類型安全。 4)枚舉支持添加方法,實現複雜邏輯。 5)嚴格類型檢查和錯誤處理可避免常見錯誤。 6)枚舉減少魔法值,提升可維護性,但需注意性能優化。

如何在系統重啟後自動設置unixsocket的權限? 如何在系統重啟後自動設置unixsocket的權限? Mar 31, 2025 pm 11:54 PM

如何在系統重啟後自動設置unixsocket的權限每次系統重啟後,我們都需要執行以下命令來修改unixsocket的權限:sudo...

解釋PHP中的晚期靜態綁定(靜態::)。 解釋PHP中的晚期靜態綁定(靜態::)。 Apr 03, 2025 am 12:04 AM

靜態綁定(static::)在PHP中實現晚期靜態綁定(LSB),允許在靜態上下文中引用調用類而非定義類。 1)解析過程在運行時進行,2)在繼承關係中向上查找調用類,3)可能帶來性能開銷。

See all articles