1、首先,为什么要使用线程特定数据呢?什么是线程特定数据。这牵涉到重入函数和不可重入函数。
重入函数就是在多个进程或者线程中,可以同时进行运行的函数,可重入函数就是不可以同时运行的函数,这个主要是可能多个进程或线程共享了一个变量,这个变量只有一个,这样同时运行的时候,就会出问题了,因为我们不知道这个静态变量具体是存入的什么值,可能刚存入一个值,立马又因为调用这个函数编程另外一个值。更加通俗的说,
在函数体内不访问那些全局变量,不使用静态局部变量,坚持只使用局部变量,写出的函数就将是可重入的。如果必须访问全局变量,记住利用互斥信号量来保护全局变量。
好了,说了这么多,如果多个线程同时访问一个函数,而这个函数恰好也必须使用一个静态变量,那么,线程特定数据就是来解决这个问题的。
2、每一个系统支持的线程特定数据元素限制不一样。POSIX要求这个限制不小于128(每一格进程)。系统为每一个进程维护一个称之为Key结构的结构数组。如图1
Key结构中额标志指示这个数组元素是否正在使用。当一个线程调用进程创建函数pthread_key_create时候,系统搜索该进程的Key结构数组找到第一个不使用的的元素,该元素的索引称之为键,返回给调用线程的正是这个索引。
除了进程范围内的Key结构数组外,系统还在进程内维护关于每一个线程的多条信息。这些特定于线程的信息我们称之为Pthread结构,如图2
pthread结构与Key结构是一一对应的。当创建一个线程的时候,系统会根据Key结构搜索,得到一个返回值,也就是Key结构的索引,每一个线程可以为该键值存储一个值(指针)。这个指针通常是通过malloc获取的。如图3。这样,当一个函数有一个静态变量的时候,多个线程同时访问,因为这个静态变量存放在键值中,就不会存在一个线程将另外一个线程的值覆盖的情况了。
因为是malloc分配的实际数据空间,所以在线程结束的时候一定注意了,要释放空间。这个可以在析构函数中进行。
线程特定函数的典型用法如下:
pthread_key_t r1_key; pthread_once_t re_once = PTHREAD_ONCE_INIT; //定义析构函数,释放空间 void readline_destructor(void *ptr) { free(ptr); } //初始化函数 void readline_once(void) { pthread_key_create(&r1_key,readline_destructor); } ssize_t readline(...) { .... pthread_once(&r1_once,readline); if((ptr = pthread_getspecific(r1_key)) == NULL) { ptr = malloc(....); //设置键值对。 pthread_setspecific(r1_key,ptr); .... } ..... }
重新复习一次。以此为证。