Thread Local Storage
我们知道多线程共享同一个进程的地址空间,对全局变量来说,某一个线程对其修改会影响其他所有线程。
如果我们需要一个变量在每个线程中都能访问,并且值在每个线程中互不影响,这就是 Thread Local Storage(TLS,也称 “线程私有数据”)。
Linux下支持两种方式定义和使用TLS变量,具体如下表:
定义方式 | 支持层次 | 访问方式 |
__thread 关键字 | 语言层面 | 与全局变量完全一样 |
pthread_key_create() 函数 | 运行库层面 |
pthread_get_specific(),读 pthread_set_specific(),写 |
__thread 关键字
看个例子,全局变量 var 被定义为 线程私有变量
#include <stdio.h> #include <stdint.h> #include <pthread.h> __thread int var = 0; // var定义为线程变量,每个数线拥有一份 void* worker(void* arg) { for (int i = 0; i < 1e4; i++) { var++; } printf("child thread [%lu] var(%p)=%d ", pthread_self(), &var, var); return 0; } int main(){ pthread_t pid1, pid2; printf("var=%d ", var); pthread_create(&pid1, NULL, worker, (void *)0); pthread_create(&pid2, NULL, worker, (void *)1); pthread_join(pid1, NULL); pthread_join(pid2, NULL); printf("main thread [%lu] var(%p)=%d ", pthread_self(), &var, var); return 0; }
运行结果
var=0 child thread [139770628466432] var(0x7f1ee2a8e6fc)=10000 child thread [139770620073728] var(0x7f1ee228d6fc)=10000 main thread [139770645350272] var(0x7f1ee3aa877c)=0
可见,主线程和两个子线程访问到的 var 变量地址不一样,每个线程对 var 都有一份自己的拷贝,不会互相影响。
pthread_key_create() 函数
也看个例子,
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <pthread.h> struct foo { int var; }; void* worker(void* arg) { pthread_key_t* key = (pthread_key_t*) arg; foo* ptr; if ((ptr = (foo*)pthread_getspecific(*key)) == NULL) { ptr = (foo*) malloc(sizeof(foo)); (void) pthread_setspecific(*key, ptr); } for (int i = 0; i < 1e4; i++) { ptr->var++; } printf("child thread [%lu] var(%p)=%d ", pthread_self(), &(ptr->var), ptr->var); return 0; } int main(){ pthread_key_t key; (void) pthread_key_create(&key, NULL); pthread_t pid1, pid2; pthread_create(&pid1, NULL, worker, (void *)(&key)); pthread_create(&pid2, NULL, worker, (void *)(&key)); pthread_join(pid1, NULL); pthread_join(pid2, NULL); foo* ptr; if ((ptr = (foo*)pthread_getspecific(key)) == NULL) { printf("main thread [%lu] var null "); } else { printf("main thread [%lu] var(%p)=%d ", pthread_self(), &(ptr->var), ptr->var); } pthread_key_delete(key); return 0; }
运行结果
child thread [140470755546880] var(0x7fc1e00008c0)=10000 child thread [140470747154176] var(0x7fc1d80008c0)=10000 main thread [140470770246240] var null
不同线程通过同一个key,访问到不同的value,可以想象 Linux 的实现会是一个map,每个线程对应不同的value。