inux多线程相关的API-(3)--线程的私有数据

私有数据的作用是,创建一个内存区域,只有本线程能够访问,其他线程无法访问。这一功能看起来很简单,没必要使用库函数来做,只要程序员自己集中注意力,分配几块内存,给不同的线程使用就行了,但是,人类不能保证这一点,稍有疏忽,可能会踩到别的线程的内存,所以,实现这一功能还是使用库函数比较可靠。

参考:《linux线程私有数据详解》、《Linux多线程基础学习(八)私有数据

1、int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));

线程的私有数据(确切的说,该数据是一个地址数据/地址值),必须绑定一个key,本函数就是用来创建这个key。创建的这个key必须是本进程内所有线程都可访问的。本函数可以保证创建的key是惟一的。不同的线程都已用同一个key来绑定自己线程的私有数据,但这并不会引起冲突。

一旦创建了key,那么本进程中的所有线程与本key关联的数据都被设置为NULL;对于新创建的线程,以前建立好的key与本线程关联的数据全部都是NULL;

本函数还可以在创建key时,为这个key关联一个析构函数destructor,如果不需要关联析构函数,可以传递NULL给形参destructor。如果为这个key关联了析构函数,且这个key关联了线程的非NULL私有地址数据(key默认关联到每个线程的NULL值),那么当线程退出时,key本身的值会被设为NULL,然后destructor函数被执行一次,destructor函数执行时的实参,就是与key关联的那个地址数据,显然该功能特别适合释放malloc分配出的内存。

形参:@key,一般我们是先定义一个变量key,然后把地址传进去;@destructor析构函数名

本函数的典型使用方法:
pthread_key_t key;//定义后key值暂不可用,必须使用pthread_key_create进行初始化后才可用
pthread_key_create(&key,tsd_destruct);

返回值:成功后,key被初始化为一个可用的值,并返回0;失败则直接返回错误号(注意,并不是设置errno!)

2、int pthread_setspecific(pthread_key_t key, const void *value);
void *pthread_getspecific(pthread_key_t key);

  一对兄弟函数,分别用于把key和地址值value绑定在一起,获取与key绑定的地址值value。
形参:@key,该值来自于pthread_key_create初始化后的key;

@value,该值一般是malloc类型函数的返回值。

注意,如果这两个函数使用的key值是未经pthread_key_create初始化的,或者是pthread_key_delete删除过的,那么pthread_setspecific和pthread_getspecific的执行结果是不可预知的。

返回值:pthread_getspecific返回与形参key绑定的地址数据,如果没有绑定,则默认为绑定的地址数据为NULL。

pthread_setspecific成功则返回0,否则直接返回错误号。

3、int pthread_key_delete(pthread_key_t key);

删除pthread_key_create创建的key,本质上说,key是无法被删除的,该函数只是使这个key变为不可用的状态。

需要注意的是,在删除key之前,程序员要考虑一下,与这个key绑定的线程私有数据,在key删除之后,会不会产生不良影响。

该函数不会触发析构函数的执行,甚至:在线程退出时,pthread_key_create函数为key关联的析构函数,也不会被执行了。

4、int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

本函数可以保证,在本进程的所有线程中,init_routine函数仅被执行一次。显然,这一功能需要一个所有线程均可访问的全局静态标志变量来记录init_routine是否被调用过,这个标志变量的地址就通过形参once_control传入。

本函数的典型应用如下:
声明所有线程均可见的全局变量:pthread_once_t once_control = PTHREAD_ONCE_INIT;
然后在不同的线程中执行:pthread_once(&once_control, my_init_func);

效果是,虽然不同的线程都执行了pthread_once函数,但my_init_func函数仅被执行了一遍。

例子:

// gcc -o private_exe private_data.c -pthread

#include <limits.h>
#include <string.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>

pthread_key_t key;

#define MB(n) (n* 1024 *1024) 

void *tthread_fun(void* args)
{
	char *addr = (char*)malloc(MB(300));
	printf("index=%d的线程malloc出的内存地址=%p\n",(int)args, addr);
	pthread_setspecific(key,addr);//绑定key与私有数据
	char *tmp = (char*)pthread_getspecific(key);//根据key取出私有数据地址
	printf("index=%d的线程与key关联的地址数据=%p\n",(int)args,tmp);
	return (void*)0;
}

void tsd_destruct(void *arg)//Thread Specified Data 线程独有(私有)数据的析构
{
	printf("线程%lu私有数据的析构函数被执行,收到的实参=%p\n",pthread_self(), arg);
	if(arg != NULL)
		free(arg);
}

int main()
{
	int ret = pthread_key_create(&key,tsd_destruct);
	if(ret != 0)
	{
		printf("创建key失败\n");
		return -1;
	}

	//pthread_key_create(&key1,NULL);

	printf("key=%d\n",key);
	pthread_t tid[3];

	int i=0;
	for(i=0;i<3;i++)
	{
		pthread_create(&tid[i],NULL,tthread_fun, (void*)(unsigned long)i);
		printf("pthread index %d's tid= %ld\n", i, (unsigned long)tid[i]);
	}
	
	for(i=0;i<3;i++)
	{
		pthread_join(tid[i],NULL);
	}
	
	return 0;
}


猜你喜欢

转载自blog.csdn.net/qq_31073871/article/details/80986132