fork多线程进程时的坑(转)
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多线程进程时的坑(转)的更多相关文章
- Linux进程管理——fork()和写时复制
写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork( )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...
- Python 多线程进程高级指南(二)
本文是如何<优雅地实现Python通用多线程/进程并行模块>的后续.因为我发现,自认为懂了一点多线程开发的皮毛,写了那么个multi_helper的玩意儿,后来才发现我靠原来就是一坨屎.自 ...
- [转]极不和谐的 fork 多线程程序
极不和谐的 fork 多线程程序 继续前几天的话题.做梦幻西游服务器优化的事情.以往的代码,定期存盘的工作分两个步骤,把 VM 里的动态数据序列化,然后把序列化后的数据写盘.这两个步骤,序列化工作并没 ...
- Hadoop在启动时的坑——start-all.sh报错
1.若你用的Linux系统是CentOS的话,这是一个坑: 它会提示你JAVA_HOME找不到,现在去修改文件: .修改hadoop配置文件,手动指定JAVA_HOME环境变量 [${hadoop_h ...
- 一起talk C栗子吧(第一百三十三回:C语言实例--创建进程时的内存细节)
各位看官们.大家好,上一回中咱们说的是从内存角度看进程和线程的样例.这一回咱们说的样例是:创建进程时的内存细节.闲话休提,言归正转.让我们一起talk C栗子吧! 看官们.我们都知道使用fork函数能 ...
- fork()和写时复制
写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当发出fork( )系统调用时,内核原样复制父进程的整个地址空间并把复制的那一份分配给子进程.这种行为是非常耗时的,因为它需要: · ...
- linux的fork()函数-进程控制
进程作为构成系统的基本细胞,不仅是系统中独立活动的实体,而且是独立竞争资源的基本实体.它要经历创建.执行.等待.终止等一系列过程. 一.fork入门知识(转载) 一个进程,包括代码.数据和分配给进程的 ...
- fork()的写时复制技术(转载)
本文转载自http://www.cnblogs.com/wuchanming/p/4495479.html,为了方便以后查看... 写时复制技术最初产生于Unix系统,用于实现一种傻瓜式的进程创建:当 ...
- Java多线程——进程和线程
Java多线程——进程和线程 摘要:本文主要解释在Java这门编程语言中,什么是进程,什么是线程,以及二者之间的关系. 部分内容来自以下博客: https://www.cnblogs.com/dolp ...
随机推荐
- Scala学习笔记(二):object、伴生对象和基本类
object object 是只有一个实例的类.它的定义与Java中的class类似,如: // 单例对象 object AppEntry { def main(args: Array[String] ...
- Thinkphp5 关联模型
必须建立两个模型分类模型(attr).文章模型(article) attr模型 <?php namespace app\common\model; use think\Model; class ...
- 【Mac brew】代理安装brew insall
http_proxy=dev-proxy.**.**:8080 https_proxy=dev-proxy.**.**:8080 brew install npm
- Tensorflow 使用slim框架下的分类模型进行分类
Tensorflow的slim框架可以写出像keras一样简单的代码来实现网络结构(虽然现在keras也已经集成在tf.contrib中了),而且models/slim提供了类似之前说过的object ...
- 如何在Ubuntu 14.04 中使用Samba共享文件
1.安装 Samba 和图形配置工具 sudo apt-get install samba samba-common system-config-samba python-glade2 gksu 2. ...
- 条件变脸pthread_cond_signal丢失问题
直接上代码: static bsem_t bsem; void* t1(void *arg) { /*printf("enter task 1\n");*/ /*while(1)* ...
- 【代码审计】XYHCMS V3.5代码执行漏洞分析
0x00 环境准备 XYHCMS官网:http://www.xyhcms.com/ 网站源码版本:XYHCMS V3.5(2017-12-04 更新) 程序源码下载:http://www.xyhc ...
- ImportError: libmysqlclient_r.so.16: cannot open shared object file: No such file or directory
在开发一个python项目是,需要用到mysql,但是, 安装完mysql-python后import加载模块提示以下错误: ImportError: libmysqlclient_r.so.16: ...
- [原]openstack-kilo--issue(十六) instance can't get ip 虚拟机不能得到ip(1)
=====问题点:vm instance不能正常获取ip地址(此时用户是:admin) =======不一样的点:如果使用用户demo用户,启动一个vm,同样的image这个时候就能正确获取ip == ...
- git diff命令详解
1 如下命令: [devel@localhost pontus]$ git diff webserver/web_pontus/app_api/v0/urls.py# 显示如下: diff --git ...