Linux中线程的高级控制-线程私有属性、线程与fork

线程私有属性
  应用程序设计中有必要提供一种变量,使得多个函数多个线程都可以访问这个变量(看起来是个全局变量),但是线程对这个变量的访问都不会彼此产生影响(貌似不是全局变量哦),但是你需要这样的数据,比如errno。那么这种数据就是线程的私有数据,尽管名字相同,但是每个线程访问的都是数据的副本。
  在使用私有数据之前,你首先要创建一个与私有数据相关的键,要来获取对私有数据的访问权限 。这个键的类型是pthread_key_t

pthread_key_create函数: 创建私有数据的键
原函数:int pthread_key_create(pthread_key_t *key, void (destructor)(void))
参数:key,创建一个键值
   (destructor)(void),析构函数
返回值:成功返回0,失败返回错误码

  创建的键放在key指向的内存单元,destructor是与键相关的析构函数。当线程调用pthread_exit或者使用return返回,析构函数就会被调用。当析构函数调用的时候,它只有一个参数,这个参数是与key关联的那个数据的地址(也就是你的私有数据啦),因此你可以在析构函数中将这个数据销毁。

**注:**键使用完之后也可以销毁,当键销毁之后,与它关联的数据并没有销毁

pthread_key_delete函数: 销毁私有数据的键
原函数:int pthread_key_delete(pthread_key_t key)
参数:key,要销毁的键
返回值:成功返回0,失败返回错误码

  有了键之后,你就可以将私有数据和键关联起来,这样就可以通过键来找到数据。所有的线程都可以访问这个键,但他们可以为键关联不同的数据。(这岂不是一个名字一样,而值却不同的全局变量么)

pthread_setspecific函数: 私有数据与键值关联函数
原函数:int pthread_setspecific(pthread_key_t key, const void *value)
参数:key,要关联的键
   value,关联的数据
返回值 :成功返回0,失败返回错误码

*pthread_getspecific函数: 获取私有数据的地址
原函数:void *pthread_getspecific(pthread_key_t key)
参数:key,关联的键值
返回值:有数据关联返回数据值,没有数据关联返回NULL

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
 
 
pthread_key_t key;
 
void *thread_fun1(void *arg)
{
        printf("thread one\n");
        int a=6;
        //将key与a关联
        pthread_setspecific(key,(void *)a);
        sleep(2);
        printf("thread one key data is %d\n",pthread_getspecific(key));
}
void *thread_fun2(void *arg)
{
        sleep(1);
        printf("thread two\n");
        int a=10;
        //将key与a关联
        pthread_setspecific(key,(void *)a);
        printf("thread two key data is %d\n",pthread_getspecific(key));
}
 
int main()
{
        pthread_t tid1,tid2;
        //创造一个key
        pthread_key_create(&key,NULL);
 
        //创造新的线程
        if(pthread_create(&tid1,NULL,thread_fun1,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }
 
        if(pthread_create(&tid2,NULL,thread_fun2,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }
        //等待线程的结束
        pthread_join(tid1,NULL);
        pthread_join(tid2,NULL);
 
        return 0;
}

运行结果

thread one
thread two
thread two key data is 10
thread one key data is 6

线程与fork
命令:ps 查看进程
命令:kii 进程inode号 (杀死一个死线程)

  当线程调用fork函数时,就为子进程创建了整个进程地址空间的副本,子进程通过继承整个地址空间的副本,也会将父进程的互斥量、读写锁、条件变量的状态继承过来。也就是说,如果父进程中互斥量是锁着的,那么在子进程中互斥量也是锁着的(尽管子进程自己还没有来得及lock),这是非常不安全的,因为不是子进程自己锁住的,它无法解锁。

  子进程内部只有一个线程,由父进程中调用fork函数的线程副本构成。如果调用fork的线程将互斥量锁住,那么子进程会拷贝一个pthread_mutex_lock副本,这样子进程就有机会去解锁了。或者互斥量根本就没被加锁,这样也是可以的,但是你不能确保永远是这样的情况。

pthread_atfork函数:
原函数:int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))
参数:(*prepare)(void),获取锁状态的函数
   (*parent)(void),fork创建的父进程
   (*child)(void),fork创建的子进程
返回值:成功 返回0,失败返回错误码

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

注: 可以有多个 pthread_atfork函数,这时也就会有多个处理程序(prepare,parent,child)。多个prepare的执行顺序与注册顺序相反,而parent和child的执行顺序与注册顺序相同。

#include <pthread.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <limits.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
 
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
//提前准备函数
void prepare()
{
        //加锁互斥量
        pthread_mutex_lock(&mutex);
        printf("prepare\n");
}
//父进程函数
void parent()
{
        //解锁互斥量
        pthread_mutex_unlock(&mutex);
        printf("parent\n");
}
//子进程函数
void child()
{
        //解锁互斥量
        pthread_mutex_unlock(&mutex);
        printf("child\n");
}
void *thread_fun(void *arg)
{
        sleep(1);
        pid_t pid;
        //调用atfork函数
        pthread_atfork(prepare,parent,child);
        //创建父子进程
        pid=fork();
        if(pid == 0)
        {
                pthread_mutex_lock(&mutex);
                printf("child process\n");
                pthread_mutex_unlock(&mutex);
        }
        if(pid > 0)
        {
                pthread_mutex_lock(&mutex);
                printf("parent process\n");
                pthread_mutex_unlock(&mutex);
        }
}
 
int main()
{
        pthread_t tid;
        //创建新的进程
        if(pthread_create(&tid,NULL,thread_fun,NULL))
        {
                printf("create new thread failure\n");
                return ;
        }
        //加锁互斥量
        pthread_mutex_lock(&mutex);
        sleep(1);
        printf("main process\n");
        //解锁互斥量
        pthread_mutex_unlock(&mutex);
        //链接新的线程
        pthread_join(tid,NULL);
        //销毁互斥量
        pthread_mutex_destroy(&mutex);
        return 0;
}

运行结果

main process
prepare
parent
child
parent process
child process
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值