add : 在fork多线程的进程时,创建的子进程只包含一个线程,该线程是调用fork函数的那个线程的副本。在man fork中,有The child process is created with a single thread—the one that called fork().这句话,亲测的确如此。在多线程进程中,为了多线程的同步及互斥,会有锁,在fork时,这些锁会一同fork到子进程中,这会导致一些问题,见下文。个人建议,最好不要fork多线程的进程,除非你有能力解决这个问题。在python的multiprocessing库中,就fork了多线程的进程。Queue中使用了线程将入队的消息放入管道,如果父进程使用了Queue.put(),那用Process()类创建子进程时,就会fork Queue类,但不会fork它里面的线程。multiprocessing的Process()创建子进程应该是解决上面提到的fork多线程进程时,锁相关的问题

import datetime
import time
import threading
import os
import thread
from multiprocessing import Process def print_thread(*args):
st = datetime.datetime.now()
while True:
now = datetime.datetime.now()
print 'thread, now={}, tid={}, pid={}'.format(str(now), thread.get_ident(), os.getpid())
time.sleep(1)
if now - st > datetime.timedelta(minutes=30):
break def print_proc(*args):
st = datetime.datetime.now()
while True:
now = datetime.datetime.now()
print 'sub process, now={}, pid={}'.format(str(now), os.getpid(), os.getppid())
time.sleep(1)
if now - st > datetime.timedelta(minutes=30):
break if __name__ == '__main__':
print 'main process, pid={}'.format(os.getpid())
t = threading.Thread(target=print_thread)
t.start()
time.sleep(2)
print 'create sub process'
p = Process(target=print_proc)
p.start()

执行结果如下:

main process, pid=5442
thread, now=2018-01-01 19:30:19.570559, tid=139746090014464, pid=5442
thread, now=2018-01-01 19:30:20.576551, tid=139746090014464, pid=5442
create sub process
thread, now=2018-01-01 19:30:21.584519, tid=139746090014464, pid=5442
sub process, now=2018-01-01 19:30:21.585514, pid=5448
thread, now=2018-01-01 19:30:22.586036, tid=139746090014464, pid=5442
sub process, now=2018-01-01 19:30:22.586514, pid=5448
thread, now=2018-01-01 19:30:23.587206, tid=139746090014464, pid=5442
sub process, now=2018-01-01 19:30:23.587485, pid=5448

原文:https://blog.codingnow.com/2011/01/fork_multi_thread.html

在 POSIX 标准中,fork 的行为是这样的:复制整个用户空间的数据(通常使用 copy-on-write 的策略,所以可以实现的速度很快)以及所有系统对象,然后仅复制当前线程到子进程。这里:所有父进程中别的线程,到了子进程中都是突然蒸发掉的。

其它线程的突然消失,是一切问题的根源。

我之前从未写过多进程多线程程序,不过公司里有 David Xu 同学(他实现维护着 FreeBSD 的线程库)是这方面的专家,今天跟徐同学讨论了一下午,终于觉得自己搞明白了其中的纠结。嗯,写点东西整理一下思路。

可能产生的最严重的问题是锁的问题。

因为为了性能,大部分系统的锁是实现在用户空间的。所以锁对象会因为 fork 复制到子进程中。

对于锁来说,从 OS 看,每个锁有一个所有者,即最后一次 lock 它的线程。

假设这么一个环境,在 fork 之前,有一个子线程 lock 了某个锁,获得了对锁的所有权。fork 以后,在子进程中,所有的额外线程都人间蒸发了。而锁却被正常复制了,在子进程看来,这个锁没有主人,所以没有任何人可以对它解锁。

当子进程想 lock 这个锁时,不再有任何手段可以解开了。程序发生死锁。

为何,POSIX 指定标准时,会定下这么一个显然不靠谱的规则?允许复制一个完全死掉的锁?答案是历史和性能。因为历史上,把锁实现在用户态是最方便的(今天依旧如此)。背后可能只需要一条原子操作指令即可。大多数 CPU 都支持的。fork 只管用户空间的复制,不会涉及其中的对象细节。

一般的惯例,多线程程序 fork 前,应该由发起 fork 的线程 lock 所有子进程可能用到的锁,fork 后,把它们一一 unlock 。当然,这样的做法就隐含了锁的次序。如果次序和平时不同,那么就会死锁。

不光是显式的使用锁,许多 CRT 函数也会间接的使用。比如 fprintf 这些文件操作。因为对 FILE * 的操作是依靠锁来达到线程安全的。最常见的问题是在子线程里调用 fprintf 写 log 。

除此之外,就是要小心一些不依赖锁的数据一致性问题了。比如若在父进程里另一个线程中操作一个链表,fork 发生时,因为其它线程的突然消失,这个链表就可能会因为只操作了一半而是不完整的数据。不过这一般不会是问题,或者可以归咎于对锁的处理。(多个线程,访问同一块数据。比如一条链表。就是需要加锁的)

fork多线程进程时的坑(转)的更多相关文章

  1. Linux进程管理——fork()和写时复制

    写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...

  2. Python 多线程进程高级指南(二)

    本文是如何<优雅地实现Python通用多线程/进程并行模块>的后续.因为我发现,自认为懂了一点多线程开发的皮毛,写了那么个multi_helper的玩意儿,后来才发现我靠原来就是一坨屎.自 ...

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

    极不和谐的 fork 多线程程序 继续前几天的话题.做梦幻西游服务器优化的事情.以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘.这两个步骤,序列化工作并没 ...

  4. Hadoop在启动时的坑——start-all.sh报错

    1.若你用的Linux系统是CentOS的话,这是一个坑: 它会提示你JAVA_HOME找不到,现在去修改文件: .修改hadoop配置文件,手动指定JAVA_HOME环境变量 [${hadoop_h ...

  5. 一起talk C栗子吧(第一百三十三回:C语言实例--创建进程时的内存细节)

    各位看官们.大家好,上一回中咱们说的是从内存角度看进程和线程的样例.这一回咱们说的样例是:创建进程时的内存细节.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们.我们都知道使用fork函数能 ...

  6. fork()和写时复制

    写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork(  )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...

  7. linux的fork()函数-进程控制

    进程作为构成系统的基本细胞,不仅是系统中独立活动的实体,而且是独立竞争资源的基本实体.它要经历创建.执行.等待.终止等一系列过程. 一.fork入门知识(转载) 一个进程,包括代码.数据和分配给进程的 ...

  8. fork()的写时复制技术(转载)

    本文转载自http://www.cnblogs.com/wuchanming/p/4495479.html,为了方便以后查看... 写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当 ...

  9. Java多线程——进程和线程

    Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...

随机推荐

  1. 前端异常监控 - BadJS

    前端异常监控 - BadJS 简介:BadJS 是 web 前端异常监控解决方案,提供一种 web 页面的脚本错误监控.上报.统计.查看等系统化的跟踪解决方案.目前BadJS覆盖了腾讯课堂.公众号.邮 ...

  2. 安装 Xshell 5/6 时出现.dll以及0xc000007错误的解决

    安装 Xshell 5/6 时出现.dll以及0xc000007错误的解决 问题:缺少 mfc110.dll或者是其他.dll文件以及应用程序运行错误,如下所示. 方法: 一种是网上直接下载.(缺少. ...

  3. Java知多少(31)static关键字以及Java静态变量和静态方法

    static 修饰符能够与变量.方法一起使用,表示是“静态”的. 静态变量和静态方法能够通过类名来访问,不需要创建一个类的对象来访问该类的静态成员,所以static修饰的成员又称作类变量和类方法.静态 ...

  4. NoSuchMethodError: ... addOnCompleteCallback

    问题描述: 使用ES 2.3.1和Spark streaming 2.1时,出现以上报错信息. 原因: addOnCompleteCallback方法在spark2.0中移除了 The addOnCo ...

  5. Python之获取微信好友信息

    save_info.py: #!/usr/bin/python # -*- coding: UTF-8 -*- import itchat import pickle itchat.auto_logi ...

  6. Mysql系列一:SQL入门

    csdn博客搬迁 连接数据库:1.在dos窗口下,进入数据库的安装目录的bin目录下,使用mysqld命令启动数据库服务,或者在计算机的服务里面启动mysql服务2.另外打开一个dos窗口,进入数据库 ...

  7. Java如何使服务器允许连接到套接字端口?

    在Java编程中,如何使服务器允许连接到套接字端口? 以下示例显示如何使服务器通过使用ServerSocket类的server.accept()方法和Socket类的sock.getInetAddre ...

  8. spring入门常见的问题及解决办法

    在学习spring过程中遇见了种种不同的异常错误,这里做了一下总结,希望遇见类似错误的同学们共勉一下. 1. 错误一 Error creating bean with name 'helloServi ...

  9. Mac 安装 Jenkins

    Mac 安装 Jenkins 有两种方法 方法一: 从官方下载最新版本:http://mirrors.jenkins-ci.org/osx/latest 点击安装. 方法二(推荐): 使用 homeb ...

  10. mysql 常用语句集

    1.查询某数据库大小语句: SELECT CONCAT(ROUND(SUM(DATA_LENGTH/1024/1024),2),'MB') AS DATA  FROM TABLES WHERE tab ...