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 发生时,因为其它线程的突然消失,这个链表就可能会因为只操作了一半而是不完整的数据。不过这一般不会是问题,或者可以归咎于对锁的处理。(多个线程,访问同一块数据。比如一条链表。就是需要加锁的)
相关文章
- python多线程,多进程编程。
- python多线程与多进程异步事件框架
- 【Python成长之路】python并发学习:多进程与多线程的用法及场景介绍
- python多线程扫描爆破网站服务器思路【笔记】
- 多线程与多进程的使用场景
- 多进程和多线程的应用场景
- 精通高并发与多线程,却不会用ThreadLocal?
- 22、多进程和多线程
- 认识多任务、多进程、单线程、多线程
- Atitit 多线程 什么时候使用多进程的选择场景 目录 1.1. 看实现,比如你的用node.js实现,那就没得选了,只能多进程1 1.2. 如果用java这一类,可以选择多进程与多线程模式,或
- 银行取款[多线程]{使用重入锁Lock接口ReentrantLock锁确保线程同步}
- ML之SVM:利用SVM算法(超参数组合进行多线程网格搜索+3fCrVa)对20类新闻文本数据集进行分类预测、评估
- JavaSE进阶 | 深入理解多线程(进程与线程、实现线程的方式、线程生命周期)
- Android FileDownloader框架多线程下载
- C++多线程基础入门
- Qt5多线程
- 生产与消费(多线程练习题)
- QT+ffmpeg+多线程的视频播放器
- 良方啊,一行代码就可提高 Python 运行速度,多线程、多进程,multiprocessing。。。
- 并发和多线程-说说面试长提平时少用的volatile
- 007-多线程-JUC线程池-Spring线程池配置、池子如何配置参数
- 001-多线程-基础-进程线程、线程状态、优先级、用户线程和守护线程
- 小白爬虫第四弹之爬虫快跑(多进程 + 多线程)
- Python中单线程、多线程和多进程的效率对比实验
- Python 一篇学会多线程