Concepts and functions
In a single-threaded program, we often use global variables to share data among multiple functions. In a multi-threaded environment, global variables are shared by all threads because data space is shared. However, sometimes it is necessary to provide a global variable private to the thread in the application design, which is only valid in a thread, but can be accessed across multiple functions. For example, the program may require each thread to maintain a linked list, the simplest way to use the same function operation is to use the thread-related data structure with the same name and different variable addresses. Such a data structure can be maintained by the POSIX thread library, known as thread-specific data (or TSD ).
Back to Top
Create and log out
POSIX defines two APIs for creating and canceling TSD:
int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *)) |
This function allocates an item from the TSD pool and assigns the value to the key for future access. If destr_function is not empty, when the thread exits (pthread_exit (), destr_function () is called with the data associated with the key as the parameter to release the allocated buffer.
No matter which thread calls pthread_key_create (), the created key is accessible to all threads, but each thread can enter different values in the key according to its own needs, this is equivalent to providing a global variable with the same name and different values. In linuxthreads implementation, the TSD pool is represented by a structure array:
static struct pthread_key_struct pthread_keys[PTHREAD_KEYS_MAX] = { { 0, NULL } }; |
Creating a TSD is equivalent to setting an item in the structure array to "in_use", returning its index to * key, and then setting the destructor function to destr_function.
To log out of a TSD instance, use the following API:
int pthread_key_delete(pthread_key_t key) |
This function does not check whether a thread is using the TSD, nor call the cleanup function (destr_function). Instead, it only releases the TSD for the next call of pthread_key_create. In linuxthreads, it also sets the related thread data item to null (see "access ").
All TSD reads and writes are performed through the special POSIX thread function. Its API definition is as follows:
int pthread_setspecific(pthread_key_t key, const void *pointer)void * pthread_getspecific(pthread_key_t key) |
When writing (pthread_setspecific (), the pointer value (not the content referred to) is associated with the key, and the corresponding READ function reads the data associated with the key. The data type is set to void *, so it can point to any type of data.
In LinuxThreads, a two-dimensional void * pointer array in the thread description structure (_ pthread_descr_struct) is used to store the data associated with the key. The array size is described by the following macros:
# Define 1_32 # define 1_\ (PTHREAD_KEYS_MAX + PTHREAD_KEY_2NDLEVEL_SIZE-1)/1_) where PTHREAD_KEYS_MAX is 1024 defined in/usr/include/bits/local_lim.h, therefore, the size of a one-dimensional array is 32. The specific storage location is calculated by the key value through the following: idx1st = key/PTHREAD_KEY_2NDLEVEL_SIZEidx2nd = key % PTHREAD_KEY_2NDLEVEL_SIZE |
That is to say, data is stored in a 32 × 32 sparse matrix. Similarly, during access, the key value is calculated similarly to obtain the index of the data location, and then the content is retrieved and returned.
Example
The following example shows how to use it and how to use this mechanism to store the private data of a thread.
#include <stdio.h>#include <pthread.h>pthread_key_t key;void echomsg(int t){ printf("destructor excuted in thread %d,param=%d\n",pthread_self(),t);}void * child1(void *arg){ int tid=pthread_self(); printf("thread %d enter\n",tid); pthread_setspecific(key,(void *)tid); sleep(2); printf("thread %d returns %d\n",tid,pthread_getspecific(key)); sleep(5);}void * child2(void *arg){ int tid=pthread_self(); printf("thread %d enter\n",tid); pthread_setspecific(key,(void *)tid); sleep(1); printf("thread %d returns %d\n",tid,pthread_getspecific(key)); sleep(5);}int main(void){ int tid1,tid2; printf("hello\n"); pthread_key_create(&key,echomsg); pthread_create(&tid1,NULL,child1,NULL); pthread_create(&tid2,NULL,child2,NULL); sleep(10); pthread_key_delete(key); printf("main thread exit\n"); return 0;} |
Create two threads for the routine and set the private data of the same thread as their own thread ID respectively. to verify their private data, the program staggered the time for writing and reading private data of the two threads, the program running result shows that the two threads do not interfere with the modification of TSD. At the same time, when the thread exits, the cleanup function is automatically executed, and the parameter is tid.