1. 程式人生 > >atfork() --同步父子程序 pthread_mutex_lock加解鎖問題

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, 即才進入子程序)。