摘要:中專門為解決線程安全的問(wèn)題抽象出了一個(gè)線程安全資源管理器,實(shí)現(xiàn)原理比較簡(jiǎn)單既然共用資源這么困難那么就干脆不共用,各線程不再共享同一份全局變量,而是各復(fù)制一份,使用數(shù)據(jù)時(shí)各線程各取自己的副本,互不干擾。
1.線程安全資源管理器
PHP的SAPI多數(shù)是單線程環(huán)境,比如cli、fpm、cgi,每個(gè)進(jìn)程只啟動(dòng)一個(gè)主線程,這種模式下是不存在線程安全問(wèn)題的,但是也有多線程的環(huán)境,比如Apache,這種情況下就需要考慮線程安全的問(wèn)題了,因?yàn)镻HP中有很多全局變量,比如最常見的:EG、CG,如果多個(gè)線程共享同一個(gè)變量將會(huì)沖突,所以PHP為多線程的應(yīng)用模型提供了一個(gè)安全機(jī)制:Zend線程安全(Zend Thread Safe, ZTS)。
PHP中專門為解決線程安全的問(wèn)題抽象出了一個(gè)線程安全資源管理器(Thread Safe Resource Mananger, TSRM),實(shí)現(xiàn)原理比較簡(jiǎn)單:既然共用資源這么困難那么就干脆不共用,各線程不再共享同一份全局變量,而是各復(fù)制一份,使用數(shù)據(jù)時(shí)各線程各取自己的副本,互不干擾。
typedef struct { size_t size; //資源的大小 ts_allocate_ctor ctor; //初始化函數(shù) ts_allocate_dtor dtor; int done; } tsrm_resource_type; struct _tsrm_tls_entry { void **storage; //資源數(shù)組 int count; //擁有的資源數(shù):storage數(shù)組大小 THREAD_T thread_id; //所屬線程id tsrm_tls_entry *next; };
如果一個(gè)資源會(huì)被多線程使用,那么首先需要預(yù)先向TSRM注冊(cè)資源,然后TSRM為這個(gè)資源分配一個(gè)唯一的編號(hào),并把這種資源的大小、初始化函數(shù)等保存到一個(gè)tsrm_resource_type結(jié)構(gòu)中,各線程只能通過(guò)TSRM分配的那個(gè)編號(hào)訪問(wèn)這個(gè)資源;然后當(dāng)線程拿著這個(gè)編號(hào)獲取資源時(shí)TSRM如果發(fā)現(xiàn)是第一次請(qǐng)求,則會(huì)根據(jù)注冊(cè)時(shí)的資源大小分配一塊內(nèi)存,然后調(diào)用初始化函數(shù)進(jìn)行初始化,并把這塊資源保存下來(lái)供這個(gè)線程后續(xù)使用。
每個(gè)線程擁有一個(gè)tsrm_tls_entry結(jié)構(gòu),當(dāng)前線程的所有資源保存在storage數(shù)組中,下標(biāo)就是各資源的id。另外所有線程的tsrm_tls_entry結(jié)構(gòu)通過(guò)一個(gè)數(shù)組保存:tsrm_tls_table,這是個(gè)全局變量,每個(gè)線程的tsrm_tls_entry結(jié)構(gòu)在這個(gè)數(shù)組中的位置是根據(jù)線程id與預(yù)設(shè)置的線程數(shù)(tsrm_tls_table_size)取模得到的,也就是說(shuō)有可能多個(gè)線程保存在tsrm_tls_table同一位置,所以tsrm_tls_entry是個(gè)鏈表,查找資源時(shí)首先根據(jù):線程id % tsrm_tls_table_size得到一個(gè)tsrm_tls_entry,然后開始遍歷鏈表比較thread_id確定是否是當(dāng)前線程的。線程本地存儲(chǔ)(Thread Local Storage, TLS),在創(chuàng)建完當(dāng)前線程的tsrm_tls_entry后會(huì)把這個(gè)值保存到當(dāng)前線程的TLS中,這樣在ts_resource()獲取資源時(shí)中就可以通過(guò)tsrm_tls_get()直接取到了,節(jié)省加鎖檢索的時(shí)間。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/28582.html
摘要:但在多線程模式下會(huì)有多個(gè),也就是說(shuō)每個(gè)線程都有一個(gè)獨(dú)立的內(nèi)存池內(nèi)存分配分配超過(guò)內(nèi)存的申請(qǐng),與通用的內(nèi)存申請(qǐng)沒有太大差別,只是將申請(qǐng)的內(nèi)存塊通過(guò)單鏈表進(jìn)行了管理。的分配實(shí)際就是分配多個(gè),的分配也是內(nèi)存分配的基礎(chǔ),它是向系統(tǒng)申請(qǐng)內(nèi)存的唯一粒度。 1.Zend內(nèi)存池 內(nèi)存池是內(nèi)核中最底層的內(nèi)存操作,定義了三種粒度的內(nèi)存塊:chunk、page、slot,每個(gè)chunk的大小為2M,page大...
摘要:插入一個(gè)元素時(shí)先將元素按先后順序插入數(shù)組,位置是,再根據(jù)的哈希值映射到散列表中的某個(gè)位置,將存入這個(gè)位置查找時(shí)先在散列表中映射到,得到在數(shù)組的位置,再?gòu)臄?shù)組中取出元素。目前只有兩種類型會(huì)使用這種機(jī)制。 1.變量結(jié)構(gòu) typedef struct _zval_struct zval; typedef union _zend_value { zend_long ...
摘要:局部變量中局部變量分配在結(jié)構(gòu)上,每次執(zhí)行都會(huì)生成一個(gè)新的,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時(shí)釋放,這是局部變量的生命周期。 1.局部變量 PHP中局部變量分配在zend_execute_data結(jié)構(gòu)上,每次執(zhí)行zend_op_array都會(huì)生成一個(gè)新的zend_execute_data,局部變量在執(zhí)行之初分配,然后在執(zhí)行結(jié)束時(shí)釋放,這是局部變量的生命周期。 讀寫操作:局部變量通過(guò)...
摘要:代碼的編譯的解析過(guò)程任務(wù)就是將代碼轉(zhuǎn)化為數(shù)組,代碼里的所有信息都保存在數(shù)組中,然后將數(shù)組交給引擎執(zhí)行,就是內(nèi)核具體執(zhí)行的命令,比如賦值加減操作函數(shù)調(diào)用等,每一條都對(duì)應(yīng)一個(gè)處理,這些是提前定義好的函數(shù)。 1.PHP代碼的編譯 PHP的解析過(guò)程任務(wù)就是將PHP代碼轉(zhuǎn)化為opcode數(shù)組,代碼里的所有信息都保存在opcode數(shù)組中,然后將opcode數(shù)組交給zend引擎執(zhí)行,opcode就是...
閱讀 1182·2021-11-23 10:10
閱讀 1518·2021-09-30 09:47
閱讀 900·2021-09-27 14:02
閱讀 2974·2019-08-30 15:45
閱讀 3024·2019-08-30 14:11
閱讀 3618·2019-08-29 14:05
閱讀 1827·2019-08-29 13:51
閱讀 2210·2019-08-29 11:33