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

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

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

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