C语言如何初始化线程局部存储实现子线程继承父线程TLS指针
问题背景
现有使用GCC __thread扩展实现线程局部存储的代码如下:
static __thread vars* ptr=NULL; void thread_init(vars* new_vars){ if(new_vars){ ptr=new_vars } printf("%p",ptr); }
__thread修饰的变量是线程私有的独立副本,线程创建时该变量会被初始化为代码中定义的静态初值,不会自动继承创建它的父线程的对应变量值。
当前测试的线程关系为:thread1是主线程创建的线程,thread1_child是thread1创建的子线程,按现有逻辑执行会出现如下结果:
- 主线程调用
thread_init(a),主线程的ptr值为a - thread1调用
thread_init(b),thread1的ptr值为b - thread1_child调用
thread_init(NULL),thread1_child的ptr值为初始值NULL,和父线程thread1的b不相等
解决方案
__thread本身没有自动继承父线程TLS值的语义,要实现子线程创建时ptr和父线程相等,只需要在创建子线程时手动把当前线程的ptr值传给子线程,子线程启动后第一时间把这个值赋值给自己的TLSptr即可。
基于POSIX线程库的可落地实现代码如下:
#include <pthread.h> #include <stdlib.h> // 原有TLS定义和thread_init逻辑保持不变 static __thread vars* ptr=NULL; void thread_init(vars* new_vars){ if(new_vars){ ptr=new_vars } printf("%p",ptr); } // 线程包装参数,用来传递继承的ptr和真实业务参数 typedef struct { vars* inherited_ptr; void* (*real_entry)(void*); void* real_arg; } thread_wrap_ctx; // 统一的线程入口包装,所有子线程启动先走这段逻辑 static void* thread_entry_wrap(void* arg) { thread_wrap_ctx* ctx = (thread_wrap_ctx*)arg; // 核心:把父线程传过来的ptr赋值给当前线程的TLS变量 ptr = ctx->inherited_ptr; void* ret = ctx->real_entry(ctx->real_arg); free(ctx); return ret; } // 封装统一的线程创建函数,自动处理ptr继承逻辑 pthread_t launch_thread(void* (*entry_func)(void*), void* arg) { pthread_t tid; thread_wrap_ctx* ctx = malloc(sizeof(thread_wrap_ctx)); // 创建子线程时,把当前线程(父线程)的ptr值塞进上下文 ctx->inherited_ptr = ptr; ctx->real_entry = entry_func; ctx->real_arg = arg; pthread_create(&tid, NULL, thread_entry_wrap, ctx); return tid; }
使用该封装后,所有通过launch_thread创建的子线程,启动时就会自动把父线程当前的ptr值设置为自己的TLSptr初值,完全满足需求:
- 主线程调用
thread_init(a)后创建thread1,thread1初始ptr就是a,之后thread1调用thread_init(b)将自身ptr更新为b - thread1调用
launch_thread创建thread1_child,thread1_child初始ptr自动设置为thread1当前的b,此时thread1_child调用thread_init(NULL)不会覆盖已有值,打印结果和父线程的b完全一致。
注意事项
- 不要跨线程直接传递TLS变量本身的地址,每个线程的TLS变量存储在线程独立的内存区域,跨线程访问属于未定义行为
- 该方案实现的是子线程创建时刻的
ptr值继承,如果后续父线程修改了自身ptr,子线程的ptr不会同步变化——这是TLS变量的设计特性,如果需要多线程实时共享同一个值,不要用TLS,改用堆上分配的共享变量配合线程同步机制即可 - 如果使用C11及以上标准编译,可以把GCC扩展
__thread替换为标准关键字_Thread_local,逻辑不需要做任何改动
内容的提问来源于stack exchange,提问作者J. Allen
相关产品推荐
相关产品推荐

