前言
為什么考慮到多線程呢?--------為了有效率的解決并發(fā)問題;
那怎么將多線程應(yīng)用結(jié)合到PHP應(yīng)用解決并發(fā)問題的過程中呢?
--------那就要回想一下“HTTP請求響應(yīng)過程了”,對PHP而言,一個典型的HTTP事務(wù):客戶端構(gòu)造請求——》http守護進程監(jiān)聽到請求,php-fpm【針對php的fast-cgi】從在初始化時就啟動的多個cgi解釋器子進程中選擇并連接到其中一個;此“幸運兒”解釋器子進程負責處理請求,比如IO訪問啦,數(shù)據(jù)庫記錄增刪改查啦等等——》cgi解釋子進程完成處理后將標準輸出和錯誤信息從同一連接返回Web Server,Web Server將處理結(jié)果構(gòu)造成響應(yīng)體返送給客戶端。
來,我們看看如上的發(fā)生過程,看看PHP代碼層我們能做些什么?
事務(wù)的第一步:客戶端并發(fā)訪問,這只是問題的根源;第二步:服務(wù)器端的http守護進程監(jiān)聽請求,并從多進程模型的php-fpm選擇子解釋器進程處理請求,比如nginx服務(wù)器是要通過負載均衡,調(diào)整各種配置(nginx.conf, php-fpm.ini, php.ini)、這顯然跟我們代碼層也沒什么關(guān)系;唯一可能有關(guān)系的地方就是子進程解釋器解釋代碼邏輯的時候了,然而php-fpm是多進程單線程模型,被選中的Only One fast-cgi解釋器子進程負責解釋PHP代碼邏輯,或者我們可以在“ Only One“的fast-cgi進程下搞些多線程做文章??;第三部Web Server將處理結(jié)果封裝響應(yīng)體,顯然代碼層在此也只能“望洋興嘆”了;
另一方面來講,進程、線程本來就是底層操作系統(tǒng)的實現(xiàn),比如常用于多線程的JAVA,它的解析過程【非解釋過程】是跑在JVM上的,在設(shè)計之初就考慮到了多線程,它的“線程”實際上是一種封裝抽象出來的概念,而PHP誰讓它是靠解釋器解釋而非編譯解析的呢?。
PHP多線程/多進程技術(shù)現(xiàn)狀
1. curl_multi多線程請求URL
curl多用于跨域請求訪問,當需要請求多個URL時,相較于for循環(huán)的curl_exec,我們可以使用curl_multi一類函數(shù)實現(xiàn)同時請求多個URL,類似多線程功能,據(jù)文檔介紹:“Allows the processing of multiple cURL handles asynchronously(異步). ? ? ? ? tips: curl_multi_select—Wait for activity on any curl_multi connection。Blocks until there is activity on any of the curl_multi connections.
使用范例可參見:http://www.cnblogs.com/chunguang/p/5895160.html
代碼實例片段如下:

2. pcntl_fork創(chuàng)建子進程
據(jù)文檔介紹:pcntl_fork()function creates a child process that differs from the parent process only in its PID and PPID. 另當其被用于Web服務(wù)環(huán)境時可能會帶來意外的結(jié)果。

至于上文提到的被用于Web服務(wù)環(huán)境時可能會帶來意外的結(jié)果,插播一則“廣告”,首先介紹下什么是“僵尸進程”。
進程調(diào)用exit之后,其并非馬上就消失掉,而是留下一個稱為“僵尸進程”(Zombie)的數(shù)據(jù)結(jié)構(gòu)。在Linux進程的5種狀態(tài)【運行,中斷,不可中斷(收到信號不喚醒和不可運行, 進程必須等待直到有中斷發(fā)生),停止,僵死】中,僵尸進程是非常特殊的一種,它已經(jīng)放棄了幾乎所 有內(nèi)存空間,沒有任何可執(zhí)行代碼,也不能被調(diào)度,僅僅在進程列表中保留一個位置,記載該進程的退出狀態(tài)等信息供其他進程收集。
所以,進程退出后,系統(tǒng)會把該進程的狀態(tài)變成Zombie,然后給上一定的時間等著父進程來收集其退出信息,因為可能父進程正忙于別的事情來不及收集,所以,使用Zombie狀態(tài)表示進程退出了,正在等待父進程收集信息中。如果需要清除這樣的進程,那么需要清除其父進程,或是等很長的時間后被內(nèi)核清除。因為 Zombie的進程還占著個進程ID號呢,這樣的進程如果很多的話,不利于系統(tǒng)的進程調(diào)度。
pcntl中多進程并發(fā)控制的用例可參見:http://blog.csdn.net/lgg201/article/details/5996444。
接著說上面的意外結(jié)果,手冊上有這么一段話:
Process Control support in PHP implements the Unix style of process creation, program execution, signal handling and process termination. Process Control should not be enabled within a web server environment and unexpected results may happen if any Process Control functions are used within a web server environment.
比如,曾經(jīng)有人嘗試過采用php提供的pcntl_fork + 管道的方式實現(xiàn)并行數(shù)據(jù)拉取與同步【http://www.cnblogs.com/bourneli/archive/2012/07/06/2579804.html】,據(jù)說標準輸出(瀏覽器)全都給到fork出的子進程,導(dǎo)致主進程無任何輸出,瀏覽器無法接收來自主進程的數(shù)據(jù)?
個人猜想:既然子進程和父進程的執(zhí)行依賴于操作系統(tǒng)調(diào)度,完全可以依賴進程間通信或者維護個父子進程關(guān)系表結(jié)構(gòu)啊,如果并行拉取的數(shù)據(jù)沒有限定次序關(guān)系的話,直接將輸出送到主進程,主進程判斷是否還有其他子進程未將輸出結(jié)果送回,而決定是否echo直接結(jié)束腳本,當然如果有次序要求,在子進程創(chuàng)建時記錄附加標志信息就可以重排序了呀。
3. multi-threading pthreads擴展
pecl擴展,安裝需要ZTS aka thread safety (線程安全模式),且只能用在CLI命令行環(huán)境下,不能在web server 環(huán)境下使用。
使用實例和范例可參見:
http://blog.csdn.net/gavin_new/article/details/65444190
http://masnun.com/2013/12/15/multithreading-in-php-doing-it-right.html
https://www.sitepoint.com/parallel-programming-pthreads-php-fundamentals/
4. swoole_client的異步模式
swoole開源項目實際上是一個網(wǎng)絡(luò)通信和異步io的引擎,一個基礎(chǔ)庫。swoole一般也是基于cli下的腳本編程。
http://rango.swoole.com/8
https://pecl.php.net/package/swoole
代碼示例:

5. yield socket_create
以同步方式書寫的異步代碼示例:
http://www.jb51.net/article/81245.htm
https://www.mullie.eu/parallel-processing-multi-tasking-php/