php 多線程有什麼問題?php
線程的併發問題。html
在多線程系統中,進程保留着資源全部權的屬性,而多個併發執行流是執行在進程中運行的線程。如Apache2 中的woker,主控制進程生成多個子進程,每一個子進程中包含固定的線程數,各個線程獨立地處理請求。一樣,爲了避免在請求到來時再生成線程,MinSpareThreads和MaxSpareThreads設置了最少和最多的空閒線程數;而MaxClients設置了全部子進程中的線程總數。若是現有子進程中的線程總數不能知足負載,控制進程將派生新的子進程。數組
當PHP運行在如上相似的多線程服務器時,此時的PHP處在多線程的生命週期中。在必定的時間內,一個進程空間中會存在多個線程,同一進程中的多個線程公用模塊初始化後的全局變量,若是和PHP在CLI模式下同樣運行腳本,則多個線程會試圖讀寫一些存儲在進程內存空間的公共資源(如在多個線程公用的模塊初始化後的函數外會存在較多的全局變量),安全
此時這些線程訪問的內存地址空間相同,當一個線程修改時,會影響其它線程,這種共享會提升一些操做的速度,可是多個線程間就產生了較大的耦合,而且當多個線程併發時,就會產生常見的數據一致性問題或資源競爭等併發常見問題,好比屢次運行結果和單線程運行的結果不同。服務器
php 線程安全版本 如何實現的?多線程
PHP引入了TSRM: 線程安全資源管理器(Thread Safe Resource Manager)。併發
TRSM 的實現代碼在 PHP 源碼的 /TSRM 目錄下,調用隨處可見,一般,咱們稱之爲 TSRM 層。通常來講,TSRM 層只會在被指明須要的時候纔會在編譯時啓用(好比,Apache2+worker MPM,一個基於線程的MPM),由於Win32下的Apache來講,是基於多線程的,因此這個層在Win32下老是被開啓的。函數
進程保留着資源全部權的屬性,線程作併發訪問,PHP中引入的TSRM層關注的是對共享資源的訪問,這裏的共享資源是線程之間共享的存在於進程的內存空間的全局變量。當PHP在單進程模式下時,一個變量被聲明在任何函數以外時,就成爲一個全局變量。spa
PHP解決併發的思路很是簡單,既然存在資源競爭,那麼直接規避掉此問題,將多個資源直接複製多份,多個線程競爭的全局變量在進程空間中各自都有一份,各作各的,徹底隔離。以標準的數組擴展爲例,首先會聲明當前擴展的全局變量,而後在模塊初始化時會調用全局變量初始化宏初始化array的,好比分配內存空間操做。線程
這裏的聲明和初始化操做都是區分ZTS和非ZTS,對於非ZTS的狀況,直接就是聲明變量,初始化變量。對於ZTS狀況,PHP內核會添加TSRM,對應到這裏的代碼就是聲明時再也不是聲明全局變量,而是用ts_rsrc_id代碼,初始化是再也不是初始化變量,而是調用ts_allocate_id函數在多線程環境中給當前這個模塊申請一個全局變量並返回資源ID。
資源ID變量名由模塊名和global_id組成。它是一個自增的整數,整個進程會共享這個變量,在進程SAPI初始調用,初始化TSRM環境時, id_count做爲一個靜態變量將被初始化爲0。這是一個很是簡單的實現,自增。確保了資源不會衝突,每一個線程的獨立。
參考文檔: