00. 目录

01. 线程之间共享数据

在多线程程序中,经常要用全局变量来实现多个函数间的数据共享。由于数据空间是共享的,因此全局变量也为所有线程共有。

测试代码:

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>
#include <stdlib.h>
 
int key = 100; //全局变量
 
void *helloworld_one(void *arg)
{
	printf("the message is %s\n",(char *)arg);
	key = 10;
	printf("key=%d, the child id is %lu\n", key, pthread_self());
	
	return NULL;
}
 
void *helloworld_two(void *arg)
{
	printf("the message is %s\n", (char *)arg);
	sleep(1);
	printf("key=%d, the child id is %lu\n", key, pthread_self());
	
	return NULL;
}
 
int main(int argc, char *argv[])
{
	pthread_t thread_id_one;
	pthread_t thread_id_two;
 
	//创建线程
	pthread_create(&thread_id_one, NULL, helloworld_one, "helloworld_one");
	pthread_create(&thread_id_two, NULL, helloworld_two, "helloworld_two");
	
	//等待线程结束,回收资源
	pthread_join(thread_id_one, NULL);
	pthread_join(thread_id_two, NULL);
	
	return 0;
}

测试结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ gcc 1.c -pthread 
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  
the message is helloworld_one
key=10, the child id is 139954627110656
the message is helloworld_two
key=10, the child id is 139954618717952
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

由运行结果可以看出,其中一个线程对全局变量的修改将影响到另一个线程的访问。

02. 线程私有数据

但有时应用程序设计中必要提供线程私有的全局变量,这个变量仅在线程中有效,但却可以跨过多个函数访问。比如在程序里可能需要每个线程维护一个链表,而会使用相同的函数来操作这个链表,最简单的方法就是使用同名而不同变量地址的线程相关数据结构。这样的数据结构可以由 Posix 线程库维护,成为线程私有数据 (Thread-specific Data,或称为 TSD)。

2.1 创建线程私有数据

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
功能:
	创建一个类型为 pthread_key_t 类型的私有数据变量( key )。
参数:
	key:在分配( malloc )线程私有数据之前,需要创建和线程私有数据相关联的键( key ),
	这个键的功能是获得对线程私有数据的访问权。
	destructor:清理函数名字( 如:fun )。当线程退出时,如果线程私有数据地址不是非 NULL,
	此函数会自动被调用。该函数指针可以设成 NULL ,这样系统将调用默认的清理函数。
    
    回调函数其定义如下:
    void fun(void *arg)
	{
    // arg 为 key 值
	}
返回值:
	成功:0
	失败:非 0

不论哪个线程调用 pthread_key_create(),所创建的 key 都是所有线程可访问,但各个线程可根据自己的需要往 key 中填入不同的值,相当于提供了一个同名不同值的变量。

2.2 销毁线程私有数据

int pthread_key_delete(pthread_key_t key);
功能:
	注销线程私有数据。这个函数并不会检查当前是否有线程正使用线程私有数据( key ),
    也不会调用清理函数 destructor() ,而只是将线程私有数据( key )释放以供下一次
    调用pthread_key_create() 使用。
参数:
	key:待注销的私有数据。
返回值:
	成功:0
	失败:非 0

2.3 关联线程私有数据成员

int pthread_setspecific(pthread_key_t key, const void *value);
功能:
	设置线程私有数据( key ) 和 value 关联,注意,是 value 的值(不是所指的内容)和 key 相关联。
参数:
	key:线程私有数据。
	value:和 key 相关联的指针。
返回值:
	成功:0
	失败:非 0

2.4 读取线程私有数据所关联的值

void *pthread_getspecific(pthread_key_t key);
功能:
	读取线程私有数据( key )所关联的值。
参数:
	key:线程私有数据。
返回值:
	成功:线程私有数据( key )所关联的值。
	失败:NULL

03. 案例实践

测试程序

// this is the test code for pthread_key 
#include <stdio.h> 
#include <pthread.h> 
 
pthread_key_t key;	// 私有数据,全局变量
 
void echomsg(void *t) 
{ 
	printf("[destructor] thread_id = %lu, param = %p\n", pthread_self(), t); 
} 
 
void *child1(void *arg) 
{ 
	int i = 10;
	
	pthread_t tid = pthread_self(); //线程号
	printf("\nset key value %d in thread %lu\n", i, tid); 
	
	pthread_setspecific(key, &i); // 设置私有数据
	
	printf("thread one sleep 2 until thread two finish\n\n");
	sleep(2); 
	printf("\nthread %lu returns %d, add is %p\n",
		tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key) ); 
} 
 
void *child2(void *arg) 
{ 
	int temp = 20;
	
	pthread_t tid = pthread_self();  //线程号
	printf("\nset key value %d in thread %lu\n", temp, tid); 
	
	pthread_setspecific(key, &temp); //设置私有数据
	
	sleep(1); 
	printf("thread %lu returns %d, add is %p\n", 
		tid, *((int *)pthread_getspecific(key)), pthread_getspecific(key)); 
} 
 
int main(void) 
{ 
	pthread_t tid1,tid2; 
	pthread_key_create(&key, echomsg); // 创建
	
	pthread_create(&tid1, NULL, child1, NULL); 
	pthread_create(&tid2, NULL, child2, NULL); 
	pthread_join(tid1, NULL);
	pthread_join(tid2, NULL);
	
	pthread_key_delete(key); // 注销
	
	return 0; 
} 

执行结果:

deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ ./a.out  

set key value 10 in thread 140551366268672

set key value 20 in thread 140551357875968
thread one sleep 2 until thread two finish

thread 140551357875968 returns 20, add is 0x7fd4a9c23ecc
[destructor] thread_id = 140551357875968, param = 0x7fd4a9c23ecc

thread 140551366268672 returns 10, add is 0x7fd4aa424ecc
[destructor] thread_id = 140551366268672, param = 0x7fd4aa424ecc
deng@itcast:/mnt/hgfs/LinuxHome/code.bak2$ 

从运行结果来看,各线程对自己的私有数据操作互不影响。也就是说,虽然 key 是同名且全局,但访问的内存空间并不是同一个。

04. 附录