TOP

PHP 源码学习之线程安全(四)
2017-10-10 11:57:32 】 浏览:10147
Tags:PHP 源码 习之 线程 安全

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_startupts_resource_exallocate_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  
		
PHP 源码学习之线程安全(四) https://www.cppentry.com/bencandy.php?fid=85&id=129772

首页 上一页 1 2 3 4 5 下一页 尾页 4/5/5
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇PHP mysqli_free_result()与mysql.. 下一篇WNMP集成环境下配置thinkPHP