lock(tsmm_mutex);
// 直接取余,将其值作为数组下标,将不同的线程散列分布在 tsrm_tls_table 中
hash_value = THREAD_HASH_OF(thread_id, tsrm_tls_table_size);
// 在 SAPI 调用 tsrm_startup 之后,tsrm_tls_table_size = expected_threads
thread_resources = tsrm_tls_table[hash_value];
if (!thread_resources) {
// 如果还没,则新分配。
allocate_new_resource(&tsrm_tls_table[hash_value], thread_id);
// 分配完毕之后再执行到下面的 else 区间
return ts_resource_ex(id, &thread_id);
} else {
do {
// 沿着链表逐个匹配
if (thread_resources->thread_id == thread_id) {
break;
}
if (thread_resources->next) {
thread_resources = thread_resources->next;
} else {
// 链表的尽头仍然没有找到,则新分配,接到链表的末尾
allocate_new_resource(&thread_resources->next, thread_id);
return ts_resource_ex(id, &thread_id);
}
} while (thread_resources);
}
TSRM_SAFE_RETURN_RSRC(thread_resources->storage, id, thread_resources->count);
// 解锁
pthread_mutex_unlock(tsmm_mutex);
}
而 allocate_new_resource
则是为新的线程在对应的链表中分配内存,并且将所有的全局变量都加入到其 storage
指针数组中。
static void allocate_new_resource(tsrm_tls_entry **thread_resources_ptr, THREAD_T thread_id)
{
int i;
(*thread_resources_ptr) = (tsrm_tls_entry *) malloc(sizeof(tsrm_tls_entry));
(*thread_resources_ptr)->storage = (void **) malloc(sizeof(void *)*id_count);
(*thread_resources_ptr)->count = id_count;
(*thread_resources_ptr)->thread_id = thread_id;
(*thread_resources_ptr)->next = NULL;
// 设置线程本地存储变量。在这里设置之后,再到 ts_resource_ex 里取
pthread_setspecific(*thread_resources_ptr);
if (tsrm_new_thread_begin_handler) {
tsrm_new_thread_begin_handler(thread_id, &((*thread_resources_ptr)->storage));
}
for (i=0; i<id_count; i++) {
if (resource_types_table[i].done) {
(*thread_resources_ptr)->storage[i] = NULL;
} else {
// 为新增的 tsrm_tls_entry 节点添加 resource_types_table 的资源
(*thread_resources_ptr)->storage[i] = (void *) malloc(resource_types_table[i].size);
if (resource_types_table[i].ctor) {
resource_types_table[i].ctor((*thread_resources_ptr)->storage[i], &(*thread_resources_ptr)->storage);
}
}
}
if (tsrm_new_thread_end_handler) {
tsrm_new_thread_end_handler(thread_id, &((*thread_resources_ptr)->storage));
}
pthread_mutex_unlock(tsmm_mutex);
}
上面有一个知识点,Thread Local Storage ,现在有一全局变量 tls_key,所有线程都可以使用它,改变它的值。 表面上看起来这是一个全局变量,所有线程都可以使用它,而它的值在每一个线程中又是单独存储的。这就是线程本地存储的意义。 那么如何实现线程本地存储呢?
需要联合 tsrm_startup
, ts_resource_ex
, allocate_new_resource
函数并配以注释一起举例说明:
// 以 pthread 为例
// 1. 首先定义了 tls_key 全局变量
static pthread_key_t tls_key;
// 2. 然后在 tsrm_startup 调用 pthread_key_create() 来创建该变量
pthread_key_create( &tls_key, 0 );
// 3. 在 allocate_new_resource 中通过 tsrm_tls_set 将 *thread_resources_ptr 指针变量存入了全局变量 tls_key 中
tsrm_tls_set(*thread_resources_ptr);// 展开之后为 pthread_setspecific(*thread_resources_ptr);
// 4. 在 ts_resource_ex 中通过 tsrm_tls_get() 获取在该线程中设置的 *thread_resources_ptr
// 多线程并发操作时,相互不会影响。
thread_resources = tsrm_tls_get();
在理解了 tsrm_tls_table
数组和其中链表的创建之后,再看 ts_resource_ex
函数中调用的这个返回宏
#define TSRM_SAFE_RETURN_RSRC(array, offset, range) \
if (offset==0) { \
return &array