pthread_atfork函数(解决在多线程程序中fork可能造成的死锁问题)

目录

1.多线程可能的死锁问题:

2.对上述问题举个例子:

3.解决方法(pthread_atfork):

3.1pthread_atfork函数:

3.2加入pthread_atfork后的代码及运行结果:


1.多线程可能的死锁问题:

思考这样一个问题:如果一个多线程程序的某个线程调用了fork函数,那么新创建的子进程是否将自动创建和父进程相同数量的线程呢?

答案是“否",正如我们期望的那样。子进程只拥有一个执行线程,它是调用fork 的那个线程的完整复制。并且子进程将自动继承父进程中互斥锁(条件变量与之类似)的状态。也就是说,父进程中已经被加锁的互斥锁在子进程中也是被锁住的。

这就引起了一个问题:子进程可能不清楚从父进程继承而来的互斥锁的具体状态(是加锁状态还是解锁状态)。这个互斥锁可能加锁了,但并不是调用fork那个线程锁住的,而是由其他线程锁住的。如果是这种情况,则子进程若再次对该互斥锁执行加锁操作就会导致死锁。

2.对上述问题举个例子:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 #include<unistd.h>
  5 #include<wait.h>
  6 
  7 pthread_mutex_t mutex;
   ///子线程运行的函数。它首先获得互斥锁mutex,然后暂停5 s,再释放该互斥锁
  8 void* fun(void*arg)
  9 {   
 10     printf("in child thread,lock mutex\n");
 11     pthread_mutex_lock(&mutex);
 12     sleep(5);
 13     pthread_mutex_unlock(&mutex);
 14 }
 15 int main()
 16 {
 17     pthread_mutex_init(&mutex,NULL);
 18     pthread_t id;
 19     pthread_create(&id,NULL,fun,NULL);
   ///*父进程中的主线程暂停1s,以确保在执行fork操作之前,子线程已经开始运行并获得了互斥变量mutex
 20     sleep(1);
 21     int pid=fork();
 22     if(pid<0)
 23     {
 24         pthread_join(id,NULL);
 25         pthread_mutex_destroy(&mutex);
 26         return 1;
 27 
 28     }
 29     else if(pid==0)
 30     {
    ///子进程从父进程继承了互斥锁mutex的状态,该互斥锁处于锁住的状态,这是由父进程中的子线程执行pthread_mutex_lock引起的,因此,下面这句加锁操作会一直阻塞,尽管从逻辑上来说它是不应该阻塞的
 31         printf("in child,get lock\n");
 32         pthread_mutex_lock(&mutex);
 33         printf("not run here\n");
 34         pthread_mutex_unlock(&mutex);
 35         exit(0);
 36 
 37     }
 38     else
 39     {
 40         wait(NULL);
 41     }
 42     pthread_join(id,NULL);
 43     pthread_mutex_destroy(&mutex);
 44     return 0;
 45 
 46 }

下面我们看一下执行结果:

 

 有执行结果可知,会出现死锁,程序不能执行下去。

3.解决方法(pthread_atfork):

3.1pthread_atfork函数:

不过,pthread提供了一个专门的函数pthread_atfork,以确保fork调用后父进程和子进程都拥有一个清楚的锁状态。该函数的定义如下:

include <pthread.h>
int pthread_atfork ( void (*prepare)(void),void (*parent) (void),void (*child) (void) );

该函数将建立3个fork句柄来帮助我们清理互斥锁的状态

  • prepare句柄将在fork调用创建出子进程之前被执行。它可以用来锁住所有父进程中的互斥锁。
  • parent句柄则是fork调用创建出子进程之后,而fork返回之前,在父进程中被执行。它的作用是释放所有在prepare句柄中被锁住的互斥锁。
  • child句柄是fork返回之前,在子进程中被执行。和parent句柄一样,child句柄也是用于释放所有在prepare句柄中被锁住的互斥锁。

该函数成功时返回0,失败则返回错误码。

 pthread_atfork函数:

void prepare()
{
  pthread_mutex_lock(&mutex);

}
void infork()
{
  pthread_mutex_unlock(&mutex);

}
pthread_atfork(prepare,infork,infork);

因此,如果上边正常工作,就应该在其中的fork调用前加入所示的代码。

3.2加入pthread_atfork后的代码及运行结果:

 1 #include<stdio.h>
  2 #include<stdlib.h>
  3 #include<pthread.h>
  4 #include<unistd.h>
  5 #include<wait.h>
  6 
  7 pthread_mutex_t mutex;
   ///子线程运行的函数。它首先获得互斥锁mutex,然后暂停5 s,再释放该互斥锁
  8 void* fun(void*arg)
  9 {   
 10     printf("in child thread,lock mutex\n");
 11     pthread_mutex_lock(&mutex);
 12     sleep(5);
 13     pthread_mutex_unlock(&mutex);
 14 }
        void prepare()
    {
          pthread_mutex_lock(&mutex);

    }
        void infork()
    {
          pthread_mutex_unlock(&mutex);

    }

 15 int main()
 16 {
 17     pthread_mutex_init(&mutex,NULL);
 18     pthread_t id;
 19     pthread_create(&id,NULL,fun,NULL);
   ///*父进程中的主线程暂停1s,以确保在执行fork操作之前,子线程已经开始运行并获得了互斥变量mutex
 20     sleep(1);
        pthread_atfork(prepare,infork,infork);
 21     int pid=fork();
 22     if(pid<0)
 23     {
 24         pthread_join(id,NULL);
 25         pthread_mutex_destroy(&mutex);
 26         return 1;
 27 
 28     }
 29     else if(pid==0)
 30     {
    ///子进程从父进程继承了互斥锁mutex的状态,该互斥锁处于锁住的状态,这是由父进程中的子线程执行pthread_mutex_lock引起的,因此,下面这句加锁操作会一直阻塞,尽管从逻辑上来说它是不应该阻塞的
 31         printf("in child,get lock\n");
 32         pthread_mutex_lock(&mutex);
 33         printf("not run here\n");
 34         pthread_mutex_unlock(&mutex);
 35         exit(0);
 36 
 37     }
 38     else
 39     {
 40         wait(NULL);
 41     }
 42     pthread_join(id,NULL);
 43     pthread_mutex_destroy(&mutex);
 44     return 0;
 45 
 46 }

执行结果:

 

由图可知,加入pthread_atfork有效避免死锁状态。 

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值