Linux多线程实践(四 )线程的特定数据

在单线程程序中,我们经常要用到"全局变量"以实现多个函数间共享数据, 然而在多线程环境下,由于数据空间是共享的,因此全局变量也为所有线程所共有。但有时应用程序设计中有必要提供线程私有的全局变量,仅在某个线程中有效,但却可以跨多个函数访问。POSIX线程库通过维护一定的数据结构来解决这个问题,这个些数据称为(Thread-specific-data或 TSD)。

相关函数如下:

[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, const void *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));    

  从上图可知:当调用pthread_key_create 后会产生一个所有线程都可见的线程特定数据(TSD)的键值(如上图中所有的线程都会得到一个pkey[1]的值), 但是这个键所指向的真实数据却是不同的,虽然都是pkey[1], 但是他们并不是指向同一块内存,而是指向了只属于自己的实际数据, 因此, 如果线程0更改了pkey[1]所指向的数据, 而并不能够影像到线程n;

   在线程调用pthread_setspecific后会将每个线程的特定数据与thread_key_t绑定起来,虽然只有一个pthread_key_t,但每个线程的特定数据是独立的内存空间,当线程退出时会执行destructor 函数。

[cpp]  view plain  copy
  1. /** 示例1:运用pthread_once, 让key只初始化一次  
  2. 注意: 将对key的初始化放入到init_routine中  
  3. **/    
  4. pthread_key_t key;    
  5. pthread_once_t once_control = PTHREAD_ONCE_INIT;    
  6. typedef struct Tsd    
  7. {    
  8.     pthread_t tid;    
  9.     char *str;    
  10. } tsd_t;    
  11.     
  12. //线程特定数据销毁函数,    
  13. //用来销毁每个线程所指向的实际数据    
  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。

特定数据;具有128项,通过key-value实现,一个线程创建一个key,其他线程也会创建,但是并不是指向的同一快内存,他们指向自己的数据,

这就是线程特定数据。

上述代码中,即使是Sleeep(2),线程1的数据并不会被线程2的数据所影响,因为是线程私有的。

当线程退出的时候会销毁2次,因为创建了两个线程。


其中tid 是线程的id,str 是传递给thread_routine 的参数,可以看到有两个不同的ptr,且destroy 调用两次。

另外,关于Linux/Unix线程私有数据实现思想:

请参考 http://blog.csdn.net/caigen1988/article/details/7901248,写的很好。

猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80232275
今日推荐