线程特定数据

2018-08-18  本文已影响0人  Junior888

Linux多线程实践(4) --线程特定数据

1. 线程特定数据是什么?

单线程 C 程序有两类基本数据:局部数据和全局数据。对于多线程 C 程序,添加了第三类
数据:线程特定数据。线程特定数据与全局数据非常相似,区别在于前者为线程专有。

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

线程特定数据基于每线程进行维护。TSD(特定于线程的数据)是定义和引用线程专用数
据的唯一方法。每个线程特定数据项都与一个作用于进程内所有线程的键关联。通过使用
key,线程可以访问基于每线程进行维护的指针 (void *)。

线程特定数据

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

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

2. 常用函数有哪些?

int pthread_key_create(pthread_key_t *key, void (*destr_function) (void *));
//分配用于标识进程中线程特定数据的键。键对进程中的所有线程来说是全局的。

int pthread_key_delete(pthread_key_t key);
//删除线程特定数据键
 
int pthread_setspecific(pthread_key_t key, const void *pointer);
//设置线程特定数据

void * pthread_getspecific(pthread_key_t key);
 //获取线程特定数据

3. 示例

/** 示例1: 设置/获取线程特定数据
在两个线程中分别设置/获取线程特定数据, 查看两个线程中的数据是否是一样的(肯定是不一样的O(∩_∩)O~)
**/
pthread_key_t key;
typedef struct Tsd
{
    pthread_t tid;
    char *str;
} tsd_t;
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
    free(value);
    cout << "destructor ..." << endl;
}
 
void *thread_routine(void *args)
{
    //设置线程特定数据
    tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
    value->tid = pthread_self();
    value->str = (char *)args;
    pthread_setspecific(key, value);
    printf("%s setspecific, address: %p\n", (char *)args, value);
 
    //获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
    sleep(2);
 
    //再次获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
 
    pthread_exit(NULL);
}
 
int main()
{
    //这样每个线程当中都会有一个key可用了,
    //但是每个key所绑定的实际区域需要每个线程自己指定
    pthread_key_create(&key, destructor_function);
 
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
    pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
 
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_key_delete(key);
 
    return 0;
}
/** 示例2:运用pthread_once, 让key只初始化一次
注意: 将对key的初始化放入到init_routine中
**/
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>

pthread_key_t key;
pthread_once_t once_control = PTHREAD_ONCE_INIT;
typedef struct Tsd
{
    pthread_t tid;
    char *str;
} tsd_t;
 
//线程特定数据销毁函数,
//用来销毁每个线程所指向的实际数据
void destructor_function(void *value)
{
    free(value);
    printf("destructor ...\n");
}
 
//初始化函数, 将对key的初始化放入该函数中,
//可以保证inti_routine函数只运行一次
void init_routine()
{
    pthread_key_create(&key, destructor_function);
    printf("init...\n");
}
 
void *thread_routine(void *args)
{
    pthread_once(&once_control, init_routine);
 
    //设置线程特定数据
    tsd_t *value = (tsd_t *)malloc(sizeof(tsd_t));
    value->tid = pthread_self();
    value->str = (char *)args;
    pthread_setspecific(key, value);
    printf("%s setspecific, address: %p\n", (char *)args, value);
 
    //获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
    sleep(2);
 
    //再次获取线程特定数据
    value = (tsd_t *)pthread_getspecific(key);
    printf("tid: 0x%x, str = %s\n", (unsigned int)value->tid, value->str);
 
    pthread_exit(NULL);
}
 
int main()
{
    pthread_t tid1, tid2;
    pthread_create(&tid1, NULL, thread_routine, (void *)"thread1");
    pthread_create(&tid2, NULL, thread_routine, (void *)"thread2");
 
    pthread_join(tid1, NULL);
    pthread_join(tid2, NULL);
    pthread_key_delete(key);
 
    return 0;
}

执行结果如下所示:

junior@ubuntu:~/thread$ ./thread_spec 
init...
thread2 setspecific, address: 0x7f2584000f30
tid: 0x8971a700, str = thread2
thread1 setspecific, address: 0x7f257c000b20
tid: 0x89f1b700, str = thread1
tid: 0x8971a700, str = thread2
destructor ...
tid: 0x89f1b700, str = thread1
destructor ...
上一篇下一篇

猜你喜欢

热点阅读