记得最早接触 PHP 的时候,经常会纠结是用 TS 还是 NTS 版的
线程安全
线程安全问题在以下情况会发生:
- 有共享变量
- 多线程
- 有修改共享变量
解决思路如下:
- 操作原子化:加锁,使用原子类
- 每个线程 copy 一份共享变量,互不影响
- 使用局部变量代替
TSRM
TSRM: 线程安全资源管理器(Thread Safe Resource Manager)
主要流程如下:
通过定义宏,未开启了 TSRM 的情况下,直接定义全局变量,如果开启的 TSRM,获取资源 ID,添加到资源 table,到内存管理 table 为每个线程分配一份该资源 size 的内存。
代码分析如下
截取了部分 TSRM 的源码(5.6 版本)
用的数据结构有三个:
- id_count:存储目前资源 id 的数量
- tsrm_resource_type:存储资源 id 对应资源的信息,资源大小,构造方法指针,析构方法指针等
- 维护存储所有线程中的资源
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| typedef struct { size_t size; ts_allocate_ctor ctor; ts_allocate_dtor dtor; int done; } tsrm_resource_type; static int resource_types_table_size; static tsrm_resource_type *resource_types_table=NULL;
struct _tsrm_tls_entry { void **storage; int count; THREAD_T thread_id; tsrm_tls_entry *next; }; static int tsrm_tls_table_size; static tsrm_tls_entry **tsrm_tls_table=NULL;
typedef int ts_rsrc_id; static ts_rsrc_id id_count;
|
为了确保资源 id 不冲突,对 id 加锁
1 2 3 4 5 6 7 8 9
| TSRM_API ts_rsrc_id ts_allocate_id(ts_rsrc_id *rsrc_id, size_t size, ts_allocate_ctor ctor, ts_allocate_dtor dtor) { tsrm_mutex_lock(tsmm_mutex); *rsrc_id = TSRM_SHUFFLE_RSRC_ID(id_count++); tsrm_mutex_unlock(tsmm_mutex); }
|
为了优化效率,在初始化的时候,也以传入 expected_threds,expected_resources 预先分配资源。
1 2 3 4 5
| tsrm_tls_table_size = expected_threads; tsrm_tls_table = (tsrm_tls_entry **) calloc(tsrm_tls_table_size, sizeof(tsrm_tls_entry *)); resource_types_table_size = expected_resources; resource_types_table = (tsrm_resource_type *) calloc(resource_types_table_size, sizeof(tsrm_resource_type));
|
参考文章
- PHP 中的线程安全
- 揭秘 TSRM-鸟哥