linux-多线程通信(五)私有数据 线程与fork

线程的私有数据

应用程序中有必要提供一种变量,使得多个函数多个线程都可以访问这个变量(如errno),但是线程对这个变量的访问都不会彼此产生影响。在使用私有数据之前,首先要创建一个私有数据相关的键,来获取对私有数据的访问权限 pthread_key_t .

  • int pthread_key_create (pthread_key_t *key, void (*destructor)(void *));
    -创建的键放在key指向的单元,destructor是与键相关的析构函数。当线程调用pthread_exit或者return返回,析构函数就会被调用。函数只有一个参数,就是与key关联的那个数据的地址(私有数据),因此可以在析构函数中将这个数据销毁。

  • int pthread_key_delete (pthread_key_t key);
    -销毁只会,与它关联的数据并没有被销毁

  • int pthread_setspecific (pthread_key_t key, const void *value);
    -将私有数据和键关联起来。其中value就是不同的线程中,key值所关联的私有数据地址,这些地址可以用malloc来分配;

  • void *pthread_getspecific (pthread_key_t key);
    -获取线程私有数据的地址,没有则返回NULL

#include "stdio.h"
#include "pthread.h"
#include "stdlib.h"
#include "string.h"
#include "unistd.h"
#include "limits.h"  
pthread_key_t key;
void *thread_fun1(void *arg)
{
	printf("thread 1 start\n");
	int a=1;
	pthread_setspecific(key,(void*)a);
	sleep(2);
	printf("thread 1 key->data is %d\n",pthread_getspecific(key));	
}

void *thread_fun2(void *arg)
{	
	sleep(1);
	printf("thread 2 start\n");
	int a=2;
	pthread_setspecific(key,(void*)a);
	
	printf("thread 2 key->data is %d\n",pthread_getspecific(key));	
	
}
int main()
{
	pthread_t tid1,tid2;
	pthread_key_create(&key,NULL);
	int err;
	err =pthread_create(&tid1,NULL,thread_fun1,NULL);
	if(err!=0)
	{
		printf("create new thread failed\n");
		return -1;
	}
	err =pthread_create(&tid2,NULL,thread_fun2,NULL);
	if(err!=0)
	{
		printf("create new thread failed\n");
		return -2;
	}
	
	pthread_join(tid1,NULL);
	pthread_join(tid2,NULL);
	pthread_key_delete(key);
	return 0;
}

有关原理:
key原理详解

线程与fork

例子1:
当线程调用fork函数时,就为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥量,读写锁,条件变量的状态继承过来。如果父进程中互斥量是锁着的,子进程的互斥量也是锁着的,这是非常不安全的,因为不是子进程自己锁住的,就无法解锁。

例子2:
子进程内部有一个线程,由父进程中调用fork函数的线程副本构成,如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_mutex_lock副本,这样子进程就有机会去解锁了。

例子3:

  • int pthread-atfork(void(*prepare)(void), void *(*parent)(void), void(*child)(void));
    -prepare是在fork调用之前会被调用的,parent在fork返回父进程之前调用,child在fork返回子进程之前调用。
    如果在prepare中加锁所有互斥量,在parent和child中解锁所有互斥量,那么fork返回之后,互斥量的状态就是未加锁的

多个prepare的执行顺序与注册顺序相反,而parent和child的执行顺序与注册顺序相同。
例子1:

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "pthread.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_fun1(void *arg)
{
	sleep(1);
	pid_t pid;
	pid = fork();
	if(pid ==0)
	{
		pthread_mutex_lock(&mutex); //之前已经在main里锁住,这里会阻塞
		printf("child\n");
		pthread_mutex_unlock(&mutex);
			
	}
	if(pid >0)
	{
		pthread_mutex_lock(&mutex); //父进程可以锁,等待main中unlokc就能解锁
		printf("parent\n");

		pthread_mutex_unlock(&mutex);
			
	}
}
int main()
{
	pthread_t tid;
	if(pthread_create(&tid,NULL,thread_fun1,NULL))
	{
		printf("create thread failed");
	}
	pthread_mutex_lock(&mutex);
	sleep(2);
	pthread_mutex_unlock(&mutex);
	printf("main\n");
	pthread_join(tid,NULL);
	return;
}

在这里插入图片描述
子进程得到的是一个加锁副本,自己不能去解锁

例子2:


#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "pthread.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
void *thread_fun1(void *arg)
{
	sleep(1);
	pid_t pid;
	pthread_mutex_lock(&mutex);
	pid = fork();
	if(pid ==0)
	{
		pthread_mutex_unlock(&mutex);// 拷贝了一个解锁副本,可以解锁
		printf("child\n");
	}
	if(pid >0)
	{
		pthread_mutex_unlock(&mutex);
		printf("parent\n");
	}
}
int main()
{
	pthread_t tid;
	if(pthread_create(&tid,NULL,thread_fun1,NULL))
	{
		printf("create thread failed");
	}

	printf("main\n");
	pthread_join(tid,NULL);
	return;

}

例子3:

#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "pthread.h"
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; //静态
void prepare()
{
	pthread_mutex_lock(&mutex);
	printf("i am prepare\n");
}
void parent()
{
	pthread_mutex_unlock(&mutex);
	printf("i am parent\n");
}
void child()
{
	pthread_mutex_unlock(&mutex);
	printf("i am child\n");
}
void *thread_fun1(void *arg)
{
	sleep(1);
	pid_t pid;
	pthread_atfork(prepare,parent,child);//先调用prepare上锁,此时两个锁。后解锁两次
	pid = fork();
	if(pid ==0)
	{
		pthread_mutex_lock(&mutex); 
		printf("child process\n");
		pthread_mutex_unlock(&mutex);		
	}
	if(pid >0)
	{
		pthread_mutex_lock(&mutex); //父进程可以锁,等待main中unlokc就能解锁
		printf("parent process\n");
		pthread_mutex_unlock(&mutex);
			
	}
}
int main()
{
	pthread_t tid;
	if(pthread_create(&tid,NULL,thread_fun1,NULL))
	{
		printf("create thread failed");
	}
	pthread_mutex_lock(&mutex);
	sleep(2);
	pthread_mutex_unlock(&mutex);
	printf("main\n");
	pthread_join(tid,NULL);
	return;
}

linux-多线程通信(五)私有数据 线程与fork_第1张图片
parent函数在fork返回父进程之前调用,child函数在fork返回子进程之前调用

你可能感兴趣的:(linux-多线程通信(五)私有数据 线程与fork)