早点打完工去睡觉

早点打完工去睡觉

目录

一、多线程+fork()

问题一:多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

1.1.1 不使用fork前,让线程函数和主程序打印其进程号

结果:

结论:

1.1.2 在主程序中加入fork

结果:

结论:

1.1.3 线程函数加入fork()

 结果:

结论:

综上所述:多线程程序fork后,子进程只启用一条执行路径,就是fork所在的执行路径。

 问题二: 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁

代码

结果

分析

解决

结果


一、多线程+fork()

问题一:多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?

1.1.1 不使用fork前,让线程函数和主程序打印其进程号

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        printf("fun pid=%d\n", getpid());
        sleep(1);
    }
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);
    for (int i = 0; i < 5; i++)
    {
        printf("main pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

结果:

Linux Day16 多线程的一些常见问题-LMLPHP

结论:

不难发现,线程函数和主函数的进程号是一样的

1.1.2 在主程序中加入fork

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    for (int i = 0; i < 5; i++)
    {
        printf("fun pid=%d\n", getpid());
        sleep(1);
    }
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);
    fork();
    for (int i = 0; i < 5; i++)
    {
        printf("main pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

结果:

Linux Day16 多线程的一些常见问题-LMLPHP

结论:

不难发现父进程中打印主线程和线程函数id=3519,而子进程执行了主线程id=3521,子进程只有一条执行路径。

1.1.3 线程函数加入fork()

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
void *fun(void *arg)
{
    fork();
    for (int i = 0; i < 5; i++)
    {
        printf("fun pid=%d\n", getpid());
        sleep(1);
    }
}
int main()
{
    pthread_t id;
    pthread_create(&id, NULL, fun, NULL);
    
    for (int i = 0; i < 5; i++)
    {
        printf("main pid=%d\n", getpid());
        sleep(1);
    }
    pthread_join(id,NULL);
    exit(0);
}

 结果:

Linux Day16 多线程的一些常见问题-LMLPHP

结论:

不难发现父进程中打印主线程和线程函数id=3551,而子进程执行了线程函数id=3553,子进程只有一条执行路径。

综上所述:多线程程序fork后,子进程只启用一条执行路径,就是fork所在的执行路径。

 问题二: 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁

多线程中fork以后产生子进程,子进程共享父进程的内容,但是会不会共享锁或者信号量呢,下面我们举个栗子。

代码

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include<sys/wait.h>
pthread_mutex_t mutex;
void*fun(void*arg)
{
    pthread_mutex_lock(&mutex);
    printf("fun lock\n");
    sleep(5);
    pthread_mutex_unlock(&mutex);
    printf("fun unlock\n");
}
int main()
{
    pthread_mutex_init(&mutex,NULL);
    pthread_t id;
    pthread_create(&id,NULL,fun,NULL);
    sleep(1);
    pid_t pid =fork();
    if(pid==-1)
    {
        exit(0);
    }
    if(pid==0)
    {
        printf("child 准备 lock\n");
        pthread_mutex_lock(&mutex);
        printf("child枷锁成功\n");
        pthread_mutex_unlock(&mutex);
        exit(0);
    }
    wait(NULL);
    printf("main over\n");
    exit(0);
}

结果

Linux Day16 多线程的一些常见问题-LMLPHP

分析

代码从主程序开始执行,执行到线程函数时,创建线程,进入fun()后,加锁,打印“fun lock”,随后睡眠5秒,我们知道多线程是有并发这个特性,这个时候就会继续主函数,进行fork,这个时候我们发现打印了"child 准备lock",注意此时我们线程函数中的锁还没有解,就有了一个新的锁,说明父进程和子进程的锁不是共用一个锁,此后5秒睡眠时间结束,这时继续执行多线程函数,解锁打印“fun unlock”,但是我们发现一件事:此函数阻塞了。

接下来就是这个问题的核心之所在。

fork()会将父进程的内容给子进程复制一份,同时也会把锁的状态给子进程,如在fork前锁还没有上,那么复制给子进程的锁就是没有上的。所以这里我们在fork前父进程就已经上了锁,传递给子进程后,子进程刚开始的锁就是上锁状态,所以就不会执行上锁状态,因为没有解锁。

解决

该函数通过3个不同阶段的回调函数来处理互斥锁状态。参数如下:
prepare:将在fork调用创建出子进程之前被执行,它可以给父进程中的互斥锁对象明明确确上锁。这个函数是在父进程的上下文中执行的,正常使用时,我们应该在此回调函数调用 pthread_mutex_lock 来给互斥锁明明确确加锁,这个时候如果父进程中的某个线程已经调用pthread_mutex_lock给互斥锁加上了锁,则在此回调中调用 pthread_mutex_lock 将迫使父进程中调用fork的线程处于阻塞状态,直到prepare能给互斥锁对象加锁为止。

parent: 是在fork调用创建出子进程之后,而fork返回之前执行,在父进程上下文中被执行。它的作用是释放所有在prepare函数中被明明确确锁住的互斥锁。
child: 是在fork返回之前,在子进程上下文中被执行。和parent处理函数一样,child函数也是用于释放所有在prepare函数中被明明确确锁住的互斥锁。

函数成功返回0, 错误返回错误码。
 

pthread_mutex_t mutex;

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

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

void * fun(void* arg)
{
    pthread_mutex_lock(&mutex);
    printf("fun lock\n");
    sleep(5);
    pthread_mutex_unlock(&mutex);
    printf("fun unlock\n");

}
int main()
{

    pthread_mutex_init(&mutex,NULL);
    pthread_t id;
    pthread_atfork(fork_lock,fork_unlock,fork_unlock);
    pthread_create(&id,NULL,fun,NULL);
    sleep(1);

    pid_t pid = fork();
    if ( pid == -1 )
    {
        exit(1);
    }

    if ( pid == 0 )
    {
        printf("child 准备lock\n");
        pthread_mutex_lock(&mutex);//阻塞
        printf("child加锁成功\n");
        pthread_mutex_unlock(&mutex);

        exit(0);
    }

    wait(NULL);
    printf("main exit\n");
    exit(0);

}

结果

Linux Day16 多线程的一些常见问题-LMLPHP

到这里线程的同步就更新这么多啦,明天更新生产者消费者模型。

09-19 07:37