Linux multi-threading practice (4) thread-specific data

In a single-threaded program, we often use "global variables" to share data among multiple functions. However, in a multi-threaded environment, since the data space is shared, global variables are also shared by all threads. But sometimes it is necessary in application design to provide thread-private global variables that are only valid within a thread but can be accessed across multiple functions . The POSIX thread library solves this problem by maintaining certain data structures called (Thread-specific-data or TSD).

The relevant functions are as follows:

[cpp]  view plain copy  
  1. int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));    
  2. int pthread_key_delete(pthread_key_t key);    
  3.     
  4. int pthread_setspecific(pthread_key_t key, constvoid *pointer);     
  5. void * pthread_getspecific(pthread_key_t key);    
  6.     
  7. pthread_once_t once_control = PTHREAD_ONCE_INIT;    
  8. int pthread_once(pthread_once_t *once_control, void (*init_routine) (void));    

  As can be seen from the above figure: when pthread_key_create is called, a key value of thread-specific data (TSD) visible to all threads will be generated (as in the above figure, all threads will get a value of pkey[1]), but this key points to The real data is different. Although they are all pkey[1], they do not point to the same piece of memory, but to their own actual data. Therefore, if thread 0 changes the data pointed to by pkey[1] , but not able to image to thread n;

   After the thread calls pthread_setspecific, the specific data of each thread is bound to thread_key_t. Although there is only one pthread_key_t, the specific data of each thread is an independent memory space, and the destructor function is executed when the thread exits.

[cpp]  view plain copy  
  1. /** Example 1: Use pthread_once to initialize the key only once  
  2. Note: Put the initialization of the key into the init_routine  
  3. **/    
  4. pthread_key_t key;    
  5. pthread_once_t once_control = PTHREAD_ONCE_INIT;    
  6. typedefstruct Tsd     
  7. {    
  8.     pthread_t tid;    
  9.     char *str;    
  10. } tsd_t;    
  11.     
  12. //Thread specific data destruction function,    
  13. //Used to destroy the actual data pointed to by each thread    
  14. void destructor_function(void *value)    
  15. {    
  16.     free(value);    
  17.     cout << "destructor ..." << endl;    
  18. }    
  19.     
  20. //初始化函数, 将对key的初始化放入该函数中,    
  21. //可以保证inti_routine函数只运行一次    
  22. void init_routine()    
  23. {    
  24.     pthread_key_create(&key, destructor_function);    
  25.     cout << "init..." << endl;    
  26. }    
  27.     
  28. void *thread_routine(void *args)    
  29. {    
  30.     pthread_once(&once_control, init_routine);    
  31.     
  32.     //设置线程特定数据    
  33.     tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));    
  34.     value->tid = pthread_self();    
  35.     value->str = (char *)args;    
  36.     pthread_setspecific(key, value);    
  37.     printf("%s setspecific, address: %p\n", (char *)args, value);    
  38.     
  39.     //获取线程特定数据    
  40.     value = (tsd_t *)pthread_getspecific(key);    
  41.     printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);    
  42.     sleep(2);    
  43.     
  44.     //再次获取线程特定数据    
  45.     value = (tsd_t *)pthread_getspecific(key);    
  46.     printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);    
  47.     
  48.     pthread_exit(NULL);    
  49. }    
  50.     
  51. int main()    
  52. {    
  53.     pthread_t tid1, tid2;    
  54.     pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");    
  55.     pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");    
  56.     
  57.     pthread_join(tid1, NULL);    
  58.     pthread_join(tid2, NULL);    
  59.     pthread_key_delete(key);    
  60.     
  61.     return 0;    
  62. }    
运行结果如下:

init....
thread1 setspecific ,address: 0x7fe7a00008c0
tid: 0xa8192700, str = thread1
thread2 setspecific ,address :0x7fe7980008c0
tid: 0xa7991700 ,str = thread2
tid: 0xa8192700 ,str = thread1
tid: 0xa7001700 ,str = thread2
destructor...
destructor...


主线程创建了两个线程然后join 等待他们退出;给每个线程的执行函数都是thread_routine,thread_routine 中调用了pthread_once,此函数表示如果当第一个线程调用它时会执行once_routine,然后从once_routine返回即pthread_once 返回,而接下去的其他线程调用它时将不再执行once_routine,此举是为了只调用pthread_key_create 一次,即产生一个pthread_key_t 值。

在thread_routine 函数中自定义了线程特定数据的类型,对于不同的线程来说TSD的内容不同,假设线程1在第一次打印完进入睡眠的时候,线程2也开始执行并调用pthread_setspecific 绑定线程2的TSD 和key_t,此时线程1调用pthread_getspecific 返回key_t 绑定的TSD指针,仍然是线程1的TSD指针,即虽然key_t 只有一个,但每个线程都有自己的TSD。

Specific data; with 128 items, implemented by key-value , one thread creates a key , and other threads also create it, but it is not the same fast memory pointed to, they point to their own data,

This is thread specific data.

In the above code, even if it is Sleep(2), the data of thread 1 will not be affected by the data of thread 2 , because it is private to the thread.

When the thread exits, it will be destroyed twice, because two threads are created.


Where tid is the id of the thread, str is the parameter passed to thread_routine, you can see that there are two different ptr, and destroy is called twice.

In addition, about Linux/Unix thread private data implementation ideas :

Please refer to http://blog.csdn.net/caigen1988/article/details/7901248, which is well written.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325781171&siteId=291194637