(最核心的东西我在下面用红色字体标出来了,理解了那块,这些东西都是就理解了!)
在本篇文章开始之前,需要大家先了解线程和进程,这位大哥讲的言简意赅:进程和线程的主要区别(总结)_kuangsonghan的博客-CSDN博客_进程与线程的区别
如标题所示,如果在多线程中的某一个线程中使用fork函数之后,fork产生的子进程会不会复制父进程中的线程呢?
答案是不会的,fork的时候只复制当前线程到子进程,下面是fork的描述:
fork(2): create child process - Linux man page
The child process is created with a single thread--the one that called fork().The entire virtual address space of the parent is replicated in the child,including the states of mutexes, condition variables, and other pthreads objects;the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.
The child process is created with a single thread--the one that called fork().The entire virtual address space of the parent is replicated in the child,
including the states of mutexes, condition variables, and other pthreads objects;the use of pthread_atfork(3) may be helpful for dealing with problems that this can cause.
下面我们来看一下下面这个例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.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 will lock \n");pthread_mutex_lock(&mutex);printf("chile lock \n");pthread_mutex_unlock(&mutex);printf("child umlock \n");exit(0);}wait(NULL);printf("main end \n");exit(0);
}
上述代码中整体的逻辑 我来给大家理一下,先开始是主线程创建了子线程,然后子线程中啥也不干,加锁----输出fun lock------睡眠五秒钟------解锁-----输出fun unlock
等不到子线程执行完,主线程又执行了fork命令,将主线程复制一份到子进程中,这会的主线程也充当了子进程的父进程,因为所有线程共享一把锁,这会子线程中的锁还在枷锁状态中,所以子进程中也就被fork将锁的加锁状态锁给复制到了子进程中,
这时候到了子进程中的时候,子进程首先打印出child will lock 然后准备加锁的时候发现,锁是夹着的状态,所以就一直会阻塞在子进程中加锁的那一步,ok,整体梳理了一遍,我们来看上述代码的运行结果:
和我上述理的思路一毛一样,最后程序一直阻塞到了子进程中加锁那步,那么这种情况怎么解决呢?
首先我们在fork的时候,我们不清楚锁的状态,所以才会造成上面的问题出现,所以我们必须在fork的时候就清楚锁的状态:
其实在Linux中人家已经帮我们写好了,他就是:pthread_atfork
atfork() --同步父子进程 pthread_mutex_lock加解锁问题_Turbyun的博客-CSDN博客
pthread_atfork(void (*prepare)(void),void (*parent)(void), void(*child)(void))
该函数中的三个参数都是指向函数的指针
第一个参数是在调用fork之前该调用的函数
第二个参数是在调用fork之后,父进程该调用的函数
第三个参数是在调用fork之后,子进程该调用的函数
ok 上述参数也交代清楚了 那么在我们上述的代码中这三个函数分别应该怎么写那?
在调用fork之前应该调用的函数:对互斥锁的加锁
void before_fork()
{pthread_mutex_lock(&mutex);
}
在调用fork之后应该调用的函数:对互斥锁的解锁
void after_fork()
{pthread_mutex_unlock(&mutex);
}
这时候 我们已经清楚锁的状态了,这种也就是清楚锁的状态的时候,fork才会进程执行,也就是推迟fork执行的时间,那么加上代码之后我们来看总代码:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <pthread.h>
#include <sys/wait.h>pthread_mutex_t mutex; //互斥锁的创建void before_fork()
{pthread_mutex_lock(&mutex);
}void after_fork()
{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_create(&id,NULL,fun,NULL);sleep(1);pthread_atfork(before_fork,after_fork,after_fork); //这行代码只要在fork之前就行pid_t pid = fork();if(pid == -1){exit(0);}if(pid == 0){printf("child will lock \n");pthread_mutex_lock(&mutex);printf("chile lock \n");pthread_mutex_unlock(&mutex);printf("child umlock \n");exit(0);}wait(NULL);printf("main end \n");exit(0);
}
然后执行的时候,fork会等待子线程解锁之后才执行:
一般来说 真实的代码中不会让子线程中sleep(5)然后再执行fork 这只是为了让我们能看的更清楚,所以才加的,真实的是子线程加锁解锁然后fork,是很快的,肉眼根本察觉不到的!