多线程程序与fork()

前言:exce调用并不创建新进程,所以前后的进程ID并未改变,exec只是用一个全新的程序替换了当前进程的正文、数据、堆和栈段

多线程程序里不准使用fork :为什么???

UNIX上C++程序设计守则3

准则3:多线程程序里不准使用fork

能引起什么问题呢?

那看看实例吧.一执行下面的代码,在子进程的执行开始处调用doit()时,发生死锁的机率会很高.

void* doit(void*) {

static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

pthread_mutex_lock(&mutex);

struct timespec ts = {10, 0}; nanosleep(&ts, 0); // 10秒寝る

// 睡10秒

pthread_mutex_unlock(&mutex);

return 0;

}

int main(void) {

pthread_t t;

pthread_create(&t, 0, doit, 0);                                 // 做成并启动子线程

if (fork() == 0) {

//子进程

//在子进程被创建的瞬间,父的子进程在执行nanosleep的场合比较多

doit(0);

return 0;

}

pthread_join(t, 0); //

// 等待子线程结束

}

以下是说明死锁的理由:

一般的,fork做如下事情

1. 父进程的内存数据会原封不动的拷贝到子进程中

2. 子进程在单线程状态下被生成

在内存区域里,静态变量mutex的内存会被拷贝到子进程里.而且,父进程里即使存在多个线程,但它们也不会被继承到子进程里. (锁状态被继承,而线程则不被继承,当然子进程的主线程可以看做一个叫进程)fork的这两个特征就是造成死锁的原因.

译者注: 死锁原因的详细解释 ---

1. 线程里的doit()先执行.

2. doit执行的时候会给互斥体变量mutex加锁.

3. mutex变量的内容会原样拷贝到fork出来的子进程中(在此之前,mutex变量的内容已经被线程改写成锁定状态).

4.子进程再次调用doit的时候,在锁定互斥体mutex的时候会发现它已经被加锁,所以就一直等待,直到拥有该互斥体的进程释放它(实际上没有人拥有这个mutex锁).

5.线程的doit执行完成之前会把自己的mutex释放,但这是的mutex和子进程里的mutex已经是两份内存.所以即使释放了mutex锁也不会对子进程里的mutex造成什么影响.

随便说一下,malloc函数就是一个维持自身固有mutex的典型例子,通常情况下它是fork-unsafe的.依赖于malloc函数的函数有很多,例如printf函数等,也是变成fork-unsafe的.

直到目前为止,已经写上了thread+fork是危险的,但是有一个特例需要告诉大家.”fork后马上调用exec的场合,是作为一个特列不会产生问题的”. 什么原因呢..?exec函数*6一被调用,进程的”内存数据”就被临时重置成非常漂亮的状态.

因此,即使在多线程状态的进程里,fork后不马上调用一切危险的函数,只是调用exec函数的话,子进程将不会产生任何的误动作.但是,请注意这里使用的”马上”这个词.即使exec前仅仅只是调用一回printf(“I’m child process”),也会有死锁的危险.

译者注:exec函数里指明的命令一被执行,该命令的内存映像就会覆盖父进程的内存空间.所以,父进程里的任何数据将不复存在.

子进程在创建后,是写时复制的,也就是子进程刚创建时,与父进程一样的副本,当exce后,那么老的地址空间被丢弃,而被新的exec的命令的内存的印像覆盖了进程的内存空间,所以锁的状态无关紧要了。

如何规避灾难呢?

为了在多线程的程序中安全的使用fork,而规避死锁问题的方法有吗?试着考虑几个.

规避方法1:做fork的时候,在它之前让其他的线程完全终止.

在fork之前,让其他的线程完全终止的话,则不会引起问题.但这仅仅是可能的情况.还有,因为一些原因而其他线程不能结束就执行了fork的时候,就会是产生出一些解析困难的不具合的问题.

规避方法2:fork后在子进程中马上调用exec函数

不用使用规避方法1的时候,在fork后不调用任何函数(printf等)就马上调用execl等,exec系列的函数.

译者注:笔者的意思可能是把原本子进程应该做的事情写成一个单独的程序,编译成可执行程序后由exec函数来调用.

规避方法3:”其他线程”中,不做fork-unsafe的处理

除了调用fork的线程,其他的所有线程不要做fork-unsafe的处理.为了提高数值计算的速度而使用线程的场合*7,这可能是fork-safe的处理,但是在一般的应用程序里则不是这样的.即使仅仅是把握了那些函数是fork-safe的,做起来还不是很容易的.fork-safe函数,必须是异步信号安全函数,而他们都是能数的过来的.因此,malloc/new,printf这些函数是不能使用的.

规避方法4:使用pthread_atfork函数,在即将fork之前调用事先准备的回调函数.apue中详细介绍了它

使用pthread_atfork函数,在即将fork之前调用事先准备的回调函数,在这个回调函数内,协商清除进程的内存数据.但是关于OS提供的函数(例:malloc),在回调函数里没有清除它的方法.因为malloc里使用的数据结构在外部是看不见的.因此,pthread_atfork函数几乎是没有什么实用价值的.

规避方法5:在多线程程序里,不使用fork

就是不使用fork的方法.即用pthread_create来代替fork.这跟规避策2一样都是比较实际的方法,值得推荐.

时间: 2024-08-04 14:25:16

多线程程序与fork()的相关文章

多线程程序中fork导致的一些问题

最近项目中,在使用多线程和多进程时,遇到了些问题. 问题描述:在多线程程序中fork出一个新进程,发现新的进程无法正常工作. 解决办法:将开线程的代码放在fork以后.也就是放在新的子进程中进行创建. 产生原因:在使用fork时会将原来进程中的所有内存数据复制一份保存在子进程中.但是在拷贝的时候,但是线程是无法被拷贝的.如果在原来线程中加了锁,在使用的时候会造成死锁.以下是具体的例子(转发): 在多线程程序里,在”自身以外的线程存在的状态”下一使用fork的话,就可能引起各种各样的问题.比较典型

谨慎使用多线程中的fork

前言 在单核时代,大家所编写的程序都是单进程/单线程程序.随着计算机硬件技术的发展,进入了多核时代后,为了降低响应时间,重复充分利用多核cpu的资源,使用多进程编程的手段逐渐被人们接受和掌握.然而因为创建一个进程代价比较大,多线程编程的手段也就逐渐被人们认可和喜爱了. 记得在我刚刚学习线程进程的时候就想,为什么很少见人把多进程和多线程结合起来使用呢,把二者结合起来不是更好吗?现在想想当初真是too young too simple,后文就主要讨论一下这个问题. 进程与线程模型 进程的经典定义就是

[转]极不和谐的 fork 多线程程序

极不和谐的 fork 多线程程序 继续前几天的话题.做梦幻西游服务器优化的事情.以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘.这两个步骤,序列化工作并没有独立在单独线程/进程里做,而是放在主线程的.IO 部分则在一个独立进程中. 序列化任务是个繁琐的过程.非常耗时(相对于 MMORPG 这个需要对用户请求快速反应的环境).当玩家同时在线人数升高时,一个简便的优化方法是把整个序列化任务分步完成,分摊到多个心跳内.这里虽然有一些数据一致性问题,但也有不

gdb 调试openmp多线程程序 示例介绍

 首先一个简单程序源代码: 1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <omp.h> 4 int main(int argc, char * argv[]){ 5 int i, k,m,n; 6 printf("i=%d,threads_num=%d\n",i, omp_get_num_threads()); 7 printf("i=%d,thread_id=%d\n&quo

多线程程序的填坑笔记和多线程编程应该遵循的规则

这几天晚上群里一朋友有偿叫我把他的程序弄稳定,因为是现场管理项目,需要做到无人职守,所以即使是客户端,也不能经常down机,因为之前对他的程序有过一个晚上的实地查看,基本流程已经有个大概的了解,我就接下来了. 刚开始的时候, 程序运行不到一个上午,内存暴涨,有时几个小时就挂了,这个那天晚上发现了,找了半天发现一处加载图片的TMemoryStream没有释放. 没想到接下来还有很多的坑需要填. 一个个解决吧,看看有多少坑! 第一天:开始他跟我说他的连接数据库老是超出数量,会导致程序不能处理任务,当

《软件调试的艺术》笔记--调试多线程程序

下面是于线程相关的GDB命令用法汇总: info threads:给出关于当前所有线程的信息. thread 3:改成线程3. break 88 thread 3 :当线程到达源代码88时停止执行. break 88 thread 3 if i == 2 当线程3到达源代码行88行,并且变量i的值为2时停止执行. 对下面的多线程进行调试: #include <stdio.h> #include <pthread.h> #include <string.h> #inclu

真正的PHP多线程(绝非fork或者用http再开进程)

转载:http://blog.csdn.net/leinchu/article/details/8012640 我写了一个扩展打算放到pecl,但是,进过交流发现有人已经做了两个php的多线程扩展 1.https://github.com/alecgorge/php_threading/ 2.https://github.com/krakjoe/pthreads 刚才成功的编译后了第二个(一个似乎不是官方的标准扩展写法.估计有点儿麻烦,而且第一个是2年前的东西了),迫不及待的写下这篇文章,介绍怎

C#OOP之十二 创建多线程程序

线程简介 在讲线程之前,我们先来看几个概念,程序.进程. 程序:为了让计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合. 进程:进程通俗的讲,就是正在执行的程序.一个进程通常就是一个正在执行的应用程序.Windows提供了"任务管理器",如下图所示: 1教室.exe为海天屏幕广播程序的进程:飞球.exe为飞球的进程:sqlserver.exe为Sql Server2005的进程:当我们启动了一个应用程序(或者说一个任务)时通常也就启动了一个进程.从上图可以看出每一个进程占一

多线程程序中操作的原子性

[转]http://www.parallellabs.com/2010/04/15/atomic-operation-in-multithreaded-application/ 多线程程序中操作的原子性 0. 背景 原子操作就 是不可再分的操作.在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制,同时也是一些常见的多线程Bug的源头.本文主要讨论了三 个问题:1. 多线程程序中对变量的读写操作是否是原子的?2. 多线程程序中对Bit field(位域)的读写操作是否是线程安全