102-多线程与 fork
来源:互联网 发布:安知我意txt百度云 编辑:程序博客网 时间:2024/05/16 07:26
在多线程程序中使用 fork,可能会导致一些意外:
- 子进程中只剩下一个线程,它是父进程中调用 fork 的线程的副本构成。这意味着在多线程环境中,会导致“线程蒸发”,莫名奇妙的失踪!
- 因为线程蒸发,它们所持有的锁也可能未释放,这将导致子进程在获取锁时进入死锁。
本文将验证这两个问题,并给出一个可行的解决方案。
1. 线程蒸发
如果在多线程环境中执行 fork,派生的子进程是单线程,子进程中的线程是由父进程中调用 fork 的那个线程的副本构成,而子进程中所有其它的线程会消失。
程序 th_fork 的功能是在父进程中创建一个线程,不断打印自己的 pid 和 ppid。创建完线程后,父进程在主线程中执行 fork,子进程每 2 秒打印一个点。
1.1 代码
// th_fork.c#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <errno.h>#define PERR(err, msg) do { errno = err; perror(msg); exit(-1); } while(0)void* fun(void* arg) { while(1) { printf("I'm %d, my father is %d\n", getpid(), getppid()); sleep(1); } return NULL;}int main() { int err; pid_t pid; pthread_t tid; pthread_create(&tid, NULL, fun, NULL); puts("parent about to fork ..."); pid = fork(); if (pid < 0) PERR(errno, "fork"); else if (pid == 0) { // child int status; err = pthread_join(tid, (void**)&status); if (err != 0) PERR(err, "pthread_join"); while(1) { puts("."); sleep(2); } exit(0); } pthread_join(tid, NULL);}
1.2 编译和运行
$ gcc th_fork.c -o th_fork -lpthread$ ./th_fork
图1 线程蒸发
从图 1 中可以看到,子进程中只有一个线程在打点,子进程的 fun 函数线程已经“蒸发”掉了。
另外,在子进程中调用了 pthread_join 函数并没有报错,看起来就好像是子进程中的 fun 函数线程已经正常结束了。
2. fork 死锁
如果在 fork 的时候,线程未释放持有的锁,将导致死锁。
程序 fork_lock 演示了这种情况。
2.1 代码
// fork_lock.c#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <pthread.h>#include <errno.h>#define PERR(err, msg) do { errno = err; perror(msg); exit(-1); } while(0)int total = 0;pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;void* fun(void* arg) { while(1) { pthread_mutex_lock(&lock); total++; puts("fun: total++"); // 时间稍稍长一点,在这个时候父进程发起 fork 就导致持锁线程蒸发 sleep(5); pthread_mutex_unlock(&lock); sleep(1); } return NULL;}int main() { int err; pid_t pid; pthread_t tid; pthread_create(&tid, NULL, fun, NULL); // 推迟 1 秒,让线程进入临界区。 sleep(1); puts("parent about to fork ..."); pid = fork(); if (pid < 0) PERR(errno, "fork"); else if (pid == 0) { // child int status; while(1) { // 由于 fun 线程蒸发,子进程获取锁就会死锁 puts("child require lock..."); pthread_mutex_lock(&lock); total++; puts("child: total++"); sleep(2); pthread_mutex_unlock(&lock); sleep(1); } exit(0); } pthread_join(tid, NULL);}
2.2 编译和运行
$ gcc fork_lock.c -o fork_lock -lpthread$ ./fork_lock
图2 fork 死锁
图 2 中,子进程在请求锁后,再也没有回应,进入死锁。
2.3 解决方案
初始的解决方案是在 fork 前先请求所有的锁,然后再 fork,另外我们希望 fork 完成后,再对所有锁进行解锁。看起来像下面这样(假设程序中只用了三个互斥量):
pthread_mutex_lock(&lock1);pthread_mutex_lock(&lock2);pthread_mutex_lock(&lock3);pid = fork();pthread_mutex_unlock(&lock1);pthread_mutex_unlock(&lock2);pthread_mutex_unlock(&lock3);if (pid < 0) { perror("fork");}else if (pid > 0) { ...}else if (pid == 0) { ...}
因此,我们将 pid = fork() 那一行改为下面三行:
pthread_mutex_lock(&lock);pid = fork();pthread_mutex_unlock(&lock);
其它的地方不变,重新编译运行,程序正常。
图2 将 fork 放入临界区,程序正常
实际上,linux 为我们提供一更加方便的机制,让我们不用每次 fork 的时候都自己加锁——atfork 函数。
关于 atfork 函数,在下一篇文章讨论,其实它所做的事,和我们的解决方案是差不多的。
3. 总结
- 理解 fork 导致的线程蒸发
- 理解 fork 死锁问题何时出现
0 0
- 102-多线程与 fork
- 多线程程序与fork()
- 多线程程序与fork() .
- 多线程与fork
- 多线程程序与fork()
- 多线程与fork
- 多线程与fork
- fork与多线程
- 多线程与fork
- 多线程程序与fork()分析
- fork()函数与Linux中的多线程编程
- fork() 函数与 Linux 中的多线程编程
- fork()函数与Linux中的多线程编程
- 多线程 fork
- fork与fork函数
- 论fork()函数与Linux中的多线程编程
- 论fork()函数与Linux中的多线程编程
- 论fork()函数与Linux中的多线程编程
- [BZOJ]1023 仙人掌图
- Java虚拟机详解----常用JVM配置参数
- [Spring实战系列]
- 如何修改element.style内联样式;
- 游戏中的英语
- 102-多线程与 fork
- 使用Python爬取最好大学网大学排名
- 算法提高 排队打水问题
- 单链表的排序
- RTOS
- 神经网络(ANN)
- firefox插件使用感受
- 快速排序
- 去掉oneNand,添加SD/MMC