atfork() --同步父子进程 pthread_mutex_lock加解锁问题

问题: 父进程先开启一个子线程,子线程中调用pthread_mutex_lock。再fork子进程,子进程同样调用pthread_mutex_lock ,导致的死锁问题。

sleep()  替换 nanosleep()   纳秒精度

    //sleep(1);                    
    struct timespec ts = {1, 0};
    nanosleep(&ts, NULL);

一、未调用atfork(),导致子进程调用pthread_mutex_lock 时发生死锁

#include <pthread.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <wait.h>

pthread_mutex_t mutex;

void *another(void *arg)
{
    printf("in child thread, lock the mutex\n");
    pthread_mutex_lock(&mutex);
    printf("parent   lock..\n");
    sleep(5);
    pthread_mutex_unlock(&mutex);
    printf("parent   unlock..\n");

    // 解锁两次,结果未定义
    //pthread_mutex_unlock(&mutex);
    //printf("mutex too.\n");
}

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

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


int main()
{
    pthread_mutex_init(&mutex, NULL);
    pthread_t id; 
    pthread_create(&id, NULL, another, NULL);

    //pthread_atfork(prepare, NULL, NULL);
#if 1
    sleep(1);
    int pid = fork();
    if (pid < 0)
    {   
        pthread_join(id, NULL);
        pthread_mutex_destroy(&mutex);
        return 1;
    }   
    else if (pid == 0)
    {   
        printf("I am in the child, want to get the lock\n");
        pthread_mutex_lock(&mutex);
        //printf("I can not run to here, oop...\n");
        printf("child   lock..\n");
        sleep(5);
        pthread_mutex_unlock(&mutex);
        printf("child   unlock..\n");
        exit(0);
    }   
    else
    {
        wait(NULL);
    }

    printf("join...\n");
#endif
    pthread_join(id, NULL);
    pthread_mutex_destroy(&mutex);
    return 0;

}

二、子进程在子线程释放锁前便可执行。

        调用atfork(), 但prepare函数为解锁,parent 和child调用的函数为加锁 (infork)

prepare:  在调用fork 生成子进程之前,先调用prepare,解锁,因此子进程便可直接调用pthread_mutex_lock(),

parent: fork调用创建出子进程之后,而fork返回之前,在父进程中被执行。

child: 在fork返回之前,在子进程中被执行

以上均成功时返回 0,失败是返回错误码。

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

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


在调用 fork之前,先调用atfork();

pthread_atfork(prepare, infork, NULL);

程序运行结果:

解析: 1、在子进程调用lock之前(即fork之前,先执行prepare),先执行prepare 解锁, 于是子进程中便可调用lock,进入sleep(5),

            2、父进程执行parent 函数 (fork调用创建出子进程之后,而fork返回之前,在父进程中被执行。)执行加锁, 父进程中的子线程睡眠之后,便可解锁

             3、pthread_atfork(), 第三个参数为NULL , 本应该为child() 即 infork()执行加锁操作,若添加,在fork返回之前,先进行lock操作(child执行),然后子进程又一次执行 lock(子进程中的lock), 便发生死锁。

三、子进程在子线程释放锁后才可执行。

        调用atfork(), 但prepare函数为加锁,parent 和child调用的函数为解锁 (infork)

void prepare(void)
{
    pthread_mutex_lock(&mutex);
}  
   
void infork(void)
{  
    pthread_mutex_unlock(&mutex);
} 
pthread_atfork(prepare, infork, infork);

空格为执行顺序,表示睡眠sleep(手动输入表示)

分析:同上, prepare先执行加锁,因此在子进程调用lock之前 就已被锁住, 待父进程中的子线程执行完毕,解锁之后,fork才返回(才开始执行 I am in the child, want to get the lock, 即才进入子进程)。

猜你喜欢

转载自blog.csdn.net/centnetHY/article/details/82491077