PHP同一進程下的多個線程會試圖讀寫一些儲存在進程記憶體空間的公用資源,此時這些線程訪問的記憶體位址空間相同,當一個線程修改時,會影響其它線程,這種共用會提高一些
操作的速度, 但是多個線程間就產生了較大的耦合,並且當多個線程並發時,就會產生常見的資料一致性問題或資源競爭等並發常見問題。
如果每個線程中對全域變數、靜態變數只有讀操作,而無寫操作,則這些個全域變數就是 安全執行緒的.
為解決線程的並發問題,PHP引入了 TSRM: 安全執行緒資源管理員(Thread Safe Resource Manager)。 TRSM 的實現代碼在 PHP 源碼的 /TSRM 目錄下,我們稱之為 TSRM 層。
我的php源碼位置(gentoo x86_64 GNU/Linux):
PHP的安全執行緒策略是將線程的共用資源複製多份,進程中的每個線程各自都有一份共用資源的拷貝,各做各的,完全隔離
原理圖:
(圖片出處:http://blog.codinglabs.org/uploads/pictures/zend-thread-safety/1.png)
_tsrm_tls_entry(TSRM )結構體:
struct _tsrm_tls_entry { void **storage; int count; THREAD_T thread_id; tsrm_tls_entry *next;};
每個tsrm_tls_entry結構負責表示一個線程的所有全域變數資源,其中thread_id儲存線程ID,count記錄全域變數數,next指向下一個節點。storage可以看做指標數組,其中每個元素是一個指向本節點代表線程的一個全域變數。最終各個線程的tsrm_tls_entry被組成一個鏈表結構,並將鏈表頭指標賦值給一個全域靜態變數tsrm_tls_table。注意,因為tsrm_tls_table是一個貨真價實的全域變數,所以所有線程會共用這個變數,這就實現了線程間的記憶體管理一致性。
(圖片出處:http://blog.codinglabs.org/articles/zend-thread-safety.html)
通過一個ts_allocate_id()函數為新的線程分配一個安全執行緒資源id(thread safe resource id):
/* allocates a new thread-safe-resource id */TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor){ int i; TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtaining a new resource id, %d bytes", size)); tsrm_mutex_lock(tsmm_mutex);//互斥鎖 /* obtain a resource id */ *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); //id_count即為當前進程下的線程數量 TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Obtained resource id %d", *rsrc_id)); /* store the new resource type in the resource sizes table */ if (resource_types_table_size < id_count) { resource_types_table = (tsrm_resource_type *) realloc(resource_types_table, sizeof(tsrm_resource_type)*id_count);//重新分配記憶體 if (!resource_types_table) { tsrm_mutex_unlock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_ERROR, "Unable to allocate storage for resource")); *rsrc_id = 0; return 0; } resource_types_table_size = id_count; } resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].size = size; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].ctor = ctor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].dtor = dtor; resource_types_table[TSRM_UNSHUFFLE_RSRC_ID(*rsrc_id)].done = 0; /* enlarge the arrays for the already active threads */ for (i=0; i<tsrm_tls_table_size; i++) { tsrm_tls_entry *p = tsrm_tls_table[i]; while (p) { if (p->count < id_count) { int j; p->storage = (void *) realloc(p->storage, sizeof(void *)*id_count); for (j=p->count; j<id_count; j++) { p->storage[j] = (void *) malloc(resource_types_table[j].size); if (resource_types_table[j].ctor) { resource_types_table[j].ctor(p->storage[j], &p->storage); } } p->count = id_count; } p = p->next; } } tsrm_mutex_unlock(tsmm_mutex); TSRM_ERROR((TSRM_ERROR_LEVEL_CORE, "Successfully allocated new resource id %d", *rsrc_id)); return *rsrc_id;}
---------------------
珠玉在前:
1.http://blog.codinglabs.org/articles/zend-thread-safety.html
2.http://blog.csdn.net/hackooo/article/details/8856225
3.http://www.laruence.com/2008/08/03/201.html