PHP的多樣化執行方式(parallel PHP多執行緒實現,原生協程實現,多程序實現,ZTS、NTS、TS又是什麼)

小松聊PHP进阶發表於2024-05-18

程序、執行緒、協程

  • 程序:應用程式的啟動例項,執行起的程式碼叫程序,有獨立的記憶體空間,類比工廠的P個(P=1單程序,P>1多程序)車間。
  • 執行緒:執行緒是CPU排程的最小單位,是程序內的執行單元,多個執行緒共享所屬程序的資源。類比車間內的T個員工(T=1單執行緒,T>1多執行緒)車間。
  • 協程:類似執行緒,協程是使用者態(CPU受限執行使用者程式指令),執行緒是核心態(CPU任意可在作業系統中任意執行任何指令)。通常在函式內部執行,並且可以在函式的不同部分之間暫停和恢復執行(最明顯的特點就是協程不一定會跟著函式的return而結束,下次呼叫時能保留上呼叫時的狀態)。

多程序、多執行緒怎麼選?

做PHP的是幾乎不用考慮的,畢竟不是C/C++這種偏底層適合做C/S或單Client的應用,PHP大多都是LAMP或LNMP的架構,結合Windows Server用IIS多執行緒的從來還沒有遇見過。

  • 多程序適用場景:
    • CPU密集型任務:對於需要大量計算的任務,多程序可以充分利用多核處理器的優勢,提高整體處理速度。
    • 獨立性要求高:如果任務之間需要完全獨立的記憶體空間和資源,避免相互影響,多程序是更好的選擇。
    • 故障隔離:多程序能夠提供更好的故障隔離性,一個程序崩潰不會影響其他程序,提高了系統的可靠性。
    • 跨平臺相容性:多程序在跨平臺相容性方面表現更好,因為不同作業系統有不同的執行緒實現,而程序模型更為統一。
  • 多執行緒適用場景:
    • I/O密集型任務:對於需要頻繁進行I/O操作的任務,多執行緒能夠更好地利用等待時間,提高系統的響應速度。
    • 資源共享:多執行緒適用於需要共享資料或資源的場景,能夠更輕鬆地實現資料共享和通訊。
    • 實時性要求高:多執行緒能夠更快速地響應事件和處理任務,適合需要實時性的應用場景。
    • 記憶體消耗較低:執行緒間共享同一程序的記憶體空間,相比多程序消耗的記憶體較少。
    • GUI程式設計:圖形介面程式通常需要不同的執行緒處理使用者互動和介面更新,多執行緒能夠提高介面的響應性和流暢性。

多執行緒實現

  • parallel:是適用於 PHP ≥ 7.2.0 的並行併發擴充套件。
  • 要求:自 parallel 1.2.0 起,要求 PHP ≥ 8.0.0,必須用ZTS版本(./configure時PHP7需要加--enable-maintainer-zts,PHP>=8需要加--enable-zts)
  • Github地址:https://github.com/krakjoe/parallel
  • 使用說明:https://www.php.net/manual/zh/intro.parallel.php
  • 下載地址:https://pecl.php.net/package/parallel
  • 應用場景:
    • 並行資料處理:對大量資料進行並行處理,如影像處理、影片處理、大規模資料分析等。
    • 網路請求並行處理:同時傳送多個網路請求,例如同時請求多個API或網頁資料,以提高資料獲取速度。
    • 平行計算:執行需要大量計算的任務,如數值計算、科學計算等。
    • 並行爬蟲:爬取網頁資料時,可以利用並行處理提高爬取效率,加快資料採集速度。
    • 多工排程:在需要同時執行多個任務的場景,可利用此擴充套件進行任務排程和執行,提高系統的併發處理能力。
  • 實操
cd /test
wget https://pecl.php.net/get/parallel-1.2.1.tgz
tar zxf parallel-1.2.1.tgz
cd parallel-1.2.1/
/usr/local/php/bin/phpize
./configure
make
make install

vim /usr/local/php/etc/php.ini
extension=parallel

常規測試,輸出@@@%%%,說明程式是從上到下依次執行的。

vim /test/a.php
<?php
for($i = 0; $i < 3; $i ++) {
    echo '@';
}

for($j = 6; $j < 9; $j ++) {
    echo '%';
}

使用擴充套件執行

<?php
$runtime = new \parallel\Runtime();

$future = $runtime->run(function(){
    for ($i = 0; $i < 25; $i ++)
        echo "@";
        return "result";
});


for ($j = 0; $j < 25; $j ++) {
    echo "%";
}

print_r($future->value());


多次執行,效果是這樣的,可見是多個執行緒交替執行。
%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php 
@%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php 
%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php 
%%%%%%%%%%%%%%%%%%%%@%@@@@@@@@@@@@@@@%%@@@@@@@@@%%result[root@localhost test]# php a.php 
%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php 
%%%%%%%%%%%%%%%%%%%%%%%%%@@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php 
%%%%%%%%%%%%%%%%%%%%%%%%@%@@@@@@@@@@@@@@@@@@@@@@@@result[root@localhost test]# php a.php

原生協程實現

  • 說明:之前的文章就有用到,把9.7GB的文字資料,按照一行一條資料的方式匯入資料庫,如果一次性載入,記憶體過載會報錯。
    所以就需要協程來幫忙,PHP的協程使用yield關鍵字來實現,在匯入過程中,就用到了,協程不一定會跟著函式的return而結束,下次呼叫時能保留上呼叫時的狀態的特點
  • 協程適用場景之一:萬字詳解PHP+Sphinx中文億級資料全文檢索實戰(實測億級資料0.1秒搜尋耗時)
  • 示例:
    • 程式碼不多,就可以實現,就例如大名鼎鼎的單例模式,也沒幾行就能實現,概念指定的程式碼不在多少,在於解決的問題。
    • 如下:yield相當於return的返回之意,且不會中斷,還能記住上次迭代的位置,所以輸出的結果是0123456789。
<?php
function func() {
    $i = 0;
    while ($i < 10) {
        yield $i;
        $i ++;
    }
}

$func = func();
foreach($func as $v) {
    echo $v;
}

多程序方式

  • 說明:最常見的執行方式,大部分企業都這種架構,不必過多演示。configure時需要新增--enable-fpm --with-fpm-user=www --with-fpm-group=www。與Nginx通訊,通常使用php-fpm(php程序管理器)去實現多個程序。
  • 解決問題:
    • 提高併發量:請求量一上來,一個程序扛不住,就多個程序一起抗。
    • 程序隔離:PHP-FPM 可以實現程序隔離,一個程序的崩潰不會影響到其他程序,提高了系統的穩定性和可靠性。
    • 提高資源利用率:PHP-FPM 可以根據實際情況調整程序池的大小,以適應伺服器負載的變化,從而更好地利用伺服器資源,避免資源浪費。
ps aux | grep php

root      3511  0.0  0.3 172232  5936 ?        Ss   Mar17   1:20 php-fpm: master process (/usr/local/php/etc/php-fpm.conf)
www       4554  0.0  0.7 178812 14516 ?        S    15:13   0:01 php-fpm: pool www
www       4555  0.0  0.7 178812 14444 ?        S    15:13   0:01 php-fpm: pool www
www       4556  0.0  0.7 178812 14500 ?        S    15:13   0:01 php-fpm: pool www
www       4596  0.0  0.7 178812 14496 ?        S    15:14   0:01 php-fpm: pool www
www       4597  0.0  0.7 178812 14556 ?        S    15:14   0:01 php-fpm: pool www

ZTS、NTS、TS版本的區別

  • ZTS:指的是 PHP 的執行緒安全版本。PHP ZTS 版本中的核心功能被修改和重新編寫,以確保在多執行緒環境下能夠安全地執行。這樣,開發人員就可以在需要在多執行緒環境中執行 PHP 指令碼時,選擇使用 PHP ZTS 版本來提高執行緒安全性和穩定性。
    總的來說,PHP ZTS(Zend Thread Safety)是 PHP 的執行緒安全版本,專門設計用於在多執行緒環境下提供更好的執行緒安全性和穩定性。

  • TS:多執行緒訪問時,採用了加鎖機制,當一個執行緒訪問某些資料時進行保護,其它執行緒不能進行訪問,直到該執行緒讀取完,其它執行緒才可使用。主要是針對windows系統iis執行的情況,如果是使用ISAPI的方式來執行PHP就必須用Thread Safe(執行緒安全)的版本。因為windows申請程序開銷較大,所以windows主要以多執行緒方式執行PHP,這時候需要執行緒安全的版本。

  • NTS:linux下,PHP使用多程序方式執行,所以可以選擇nts版本。
    ISAPI全稱Internet Server Application Programming Interface,是一種微軟Windows平臺下的Web伺服器擴充套件技術。它允許開發者編寫動態網頁應用程式並直接執行在Web伺服器上,可以與Web伺服器(如IIS)直接互動,提供更高效的效能和更靈活的功能擴充套件。透過ISAPI,開發者可以使用C++、Delphi等語言編寫高效能的Web應用程式。

  • 如果要開多執行緒:ZTS優於TS優於NTS。

相關文章