muduo库-Threadlocal类
muduo库-Threadlocal类
首先来看一个概念:线程特定数据
在单线程程序中,我们经常用全局变量共享数据。多线程环境下,全部变量被所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效。POSIX线程库通过维护一定的数据结构来解决这个问题,这些数据称之为线程特定数据(Thread-specific Data,或TSD)。对于POD类型,可以用__thread来解决。
POD类型
POD(Plain Old Data)类型是C++ 定义的一类数据结构概念,比如 int、float 等都是 POD 类型的。Plain 代表它是一个普通类型,Old 代表它是旧的,与几十年前的 C 语言兼容,那么就意味着可以使用 memcpy() 这种最原始的函数进行操作。两个系统进行交换数据,如果没有办法对数据进行语义检查和解释,那就只能以非常底层的数据形式进行交互,而拥有 POD 特征的类或者结构体通过二进制拷贝后依然能保持数据结构不变。也就是说,能用 C 的 memcpy() 等函数进行操作的类、结构体就是 POD 类型的数据。
POSIX线程库通过四个函数操作线程特定数据,分别是pthread_key_create,pthread_key_delete,pthread_getspecific,pthread_setspecific。

一旦某个线程创建了一个key,比如key[1],那么其他线程也有自己的key[1],它们通过各自的key[1]访问到的实际数据(堆上内存分配的空间)是不同的,pthread_key_delete
只是删除key,实际数据空间的释放需要在pthread_key_create中注册一个回调函数destructor去delete T*。
ThreadLocal类结构图如下:

类的实现:
1 | |
muduo库注册的destroy()函数直接将它的入口参数x指针强制转化为T类型指针。然后直接调用delete。这是什么原因呢?
在pthread_key_create()函数中,传入了一个&ThreadLocal::destructor的成员指针,当线成局部变量销毁时,如果传入的第二个参数不为NULL,系统将调用该函数取销毁实际的数据。由于是类成员函数,隐藏了一个指针是this指针,这时候void *x,x的地址实际上就是this的地址,也就是该对象的地址。
上面我们知道,ThreadLocal类只有一个成员,就是pthread_key_t类型成员pkey_(不是指针类型),我们可以再回头看pthread_key_create()函数,我们发现它传入的第一个参数正是pkey_的地址,也就是pthread_key_t*类型。所以我们在destructor()函数中使用
1 | |
由于pkey_是传入pthread_key_create()的第一个参数,所以它的地址就是实际数据存放的地址。而(void*)this == (void*)&pkey_,所以直接强制转化this指针为T*类型,然后以T*类型的方式delete,就会真实释放线程局部存储的数据。
参考: