目录
问题一:多线程中某个线程调用 fork(),子进程会有和父进程相同数量的线程吗?
1.1.1 不使用fork前,让线程函数和主程序打印其进程号
综上所述:多线程程序fork后,子进程只启用一条执行路径,就是fork所在的执行路径。
问题二: 父进程被加锁的互斥锁 fork 后在子进程中是否已经加锁
- #include
- #include
- #include
- #include
- #include
- 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);
- }
不难发现,线程函数和主函数的进程号是一样的
- #include
- #include
- #include
- #include
- #include
- 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);
- }
不难发现父进程中打印主线程和线程函数id=3519,而子进程执行了主线程id=3521,子进程只有一条执行路径。
- #include
- #include
- #include
- #include
- #include
- 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);
- }
不难发现父进程中打印主线程和线程函数id=3551,而子进程执行了线程函数id=3553,子进程只有一条执行路径。
多线程中fork以后产生子进程,子进程共享父进程的内容,但是会不会共享锁或者信号量呢,下面我们举个栗子。
- #include
- #include
- #include
- #include
- #include
- #include
- 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);
- }
代码从主程序开始执行,执行到线程函数时,创建线程,进入fun()后,加锁,打印“fun lock”,随后睡眠5秒,我们知道多线程是有并发这个特性,这个时候就会继续主函数,进行fork,这个时候我们发现打印了"child 准备lock",注意此时我们线程函数中的锁还没有解,就有了一个新的锁,说明父进程和子进程的锁不是共用一个锁,此后5秒睡眠时间结束,这时继续执行多线程函数,解锁打印“fun unlock”,但是我们发现一件事:此函数阻塞了。
接下来就是这个问题的核心之所在。
fork()会将父进程的内容给子进程复制一份,同时也会把锁的状态给子进程,如在fork前锁还没有上,那么复制给子进程的锁就是没有上的。所以这里我们在fork前父进程就已经上了锁,传递给子进程后,子进程刚开始的锁就是上锁状态,所以就不会执行上锁状态,因为没有解锁。
int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void));
该函数通过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);
-
- }
到这里线程的同步就更新这么多啦,明天更新生产者消费者模型。