這篇文章的內容介紹的是關於PHP7核心剖析10之線程安全,現在分享給大家,有需要的朋友可以參考一下
1.線程安全資源管理器
PHP的SAPI多數是單線程環境,例如cli、fpm、cgi,每個行程只啟動一個主線程,這種模式下是不存在線程安全問題的,但是也有多線程的環境,例如Apache,這種情況下就需要考慮線程安全的問題了,因為PHP中有很多全局變量,比如最常見的:EG、CG,如果多個線程共享同一個變量將會衝突,所以PHP為多線程的應用模型提供了一個安全機制:Zend線程安全(Zend Thread Safe, ZTS)。
PHP中專門為解決線程安全的問題抽像出了一個線程安全資源管理器(Thread Safe Resource Mananger, TSRM),實現原理比較簡單:既然共用資源這麼困難那麼就乾脆不共用,各執行緒不再共享同一份全域變量,而是各複製一份,使用資料時各執行緒各取自己的副本,互不干擾。
typedef struct { size_t size; //资源的大小 ts_allocate_ctor ctor; //初始化函数 ts_allocate_dtor dtor; int done; } tsrm_resource_type; struct _tsrm_tls_entry { void **storage; //资源数组 int count; //拥有的资源数:storage数组大小 THREAD_T thread_id; //所属线程id tsrm_tls_entry *next; };
如果一個資源會被多執行緒使用,那麼首先需要預先向TSRM註冊資源,然後TSRM為這個資源分配一個唯一的編號,並把這種資源的大小、初始化函數等保存到一個tsrm_resource_type結構中,各線程只能透過TSRM分配的那個編號訪問這個資源;然後當線程拿著這個編號獲取資源時TSRM如果發現是第一次請求,則會根據註冊時的資源大小分配一塊內存,然後呼叫初始化函數進行初始化,並把這塊資源保存下來供這個執行緒後續使用。
每個執行緒擁有一個tsrm_tls_entry結構,目前執行緒的所有資源都保存在storage陣列中,下標就是各資源的id。另外所有執行緒的tsrm_tls_entry結構透過一個陣列保存:tsrm_tls_table,這是個全域變數,每個執行緒的tsrm_tls_entry結構在這個陣列中的位置是根據執行緒id與預先設定的執行緒數(tsrm_tls_table_size)取模得到的,也就是說有可能多個執行緒保存在tsrm_tls_table同一位置,所以tsrm_tls_entry是個鍊錶,在尋找資源時先根據:執行緒id % tsrm_tls_table_size得到一個tsrm_tls_entry,然後開始遍歷鍊錶比較thread_id確定是否是目前執行緒的。線程本地存儲(Thread Local Storage, TLS),在創建完當前線程的tsrm_tls_entry後會把這個值保存到當前線程的TLS中,這樣在ts_resource()獲取資源時中就可以透過tsrm_tls_get()直接取到了,節省加鎖檢索的時間。
#相關推薦:
##
以上是PHP7核心剖析10之線程安全的詳細內容。更多資訊請關注PHP中文網其他相關文章!