之前两篇文章讨论了进程意外退出时,如何杀死子进程,这节我们研究下在使用进程池multiprocessing.Pool时,如何保证主进程意外退出,进程池中的worker进程同时退出,不产生孤儿进程。如果对python标准库进程池不清楚的园友,可以看下之前写的几篇文章。我们尝试下主进程中使用进程池,看看worker进程是否会退出:

1 importtime2 importos3 importsignal4 from multiprocessing importPool5

6

7 deffun(x):8 print 'current sub-process pid is %s' %os.getpid()9 whileTrue:10 print 'args is %s' %x11 time.sleep(1)12

13 defterm(sig_num, addtion):14 print 'current pid is %s, group id is %s' %(os.getpid(), os.getpgrp())15 os.killpg(os.getpgid(os.getpid()), signal.SIGKILL)16

17 if __name__ == '__main__':18 print 'current pid is %s' %os.getpid()19 mul_pool =Pool()20 signal.signal(signal.SIGTERM, term)21

22 for i in range(3):23 mul_pool.apply_async(func=fun, args=(str(i),))

运行上面的代码,发现在我还没来得及通过kill命令发送SIGTERM时,进程竟然退出了,而且主进程和进程池中的worker进程都退出了。结合线程的特征想了下,可能在新建worker进程时,默认启动方式为daemon。通过查看源码,发现worker进程启动之前,被设置为daemon=True,也就是说主进程不会等待worker进程执行完再退出,这种情况下worker进程作为主进程的子进程,会随着主进程的退出而退出,部分源码如下:

1 w = self.Process(target=worker,2 args=(self._inqueue, self._outqueue,3 self._initializer,4 self._initargs, self._maxtasksperchild)5 )6 self._pool.append(w)7 w.name = w.name.replace('Process', 'PoolWorker')8 w.daemon =True9 w.start()

接着我手动改了下源码,将daemon设置为False,接着启动进程,发现现象依然如前,程序刚启动紧接着就全部退出(主进程和子进程)。很奇怪,难道daemon表示的含义在进程和线程中有不同?联系之前对进程池分析的两篇文章,发现进程池中的几个线程在启动之前也被设置为daemon=True,继续手动修改下源码,将线程的daemon设置为False,再次启动进程,这次进程持续运行,主进程并未退出,通过kill命令发送SIGTERM信号后,整个进程组退出。编码中,我们当然不能去修改源码了,标准库中的Pool提供了一个join方法,它可以对进程池中的线程以及worker进程进行等待,注意在调用join之前调用close方法保证进程池不在接收新任务。我们在对上面的代码进行一些修改:

1 if __name__ == '__main__':2 print 'current pid is %s' %os.getpid()3 mul_pool =Pool()4 signal.signal(signal.SIGTERM, term)5

6 for i in range(3):7 mul_pool.apply_async(func=fun, args=(str(i),))8

9 mul_pool.close()10 mul_pool.join()

改过之后程序不会自动退出了,但是又出现了新的问题,向进程发送kill命令,进程并没有捕获到信号,仍然继续运行。在stackoverflow找到了类似的问题,对标准库中signal有如下描述:A Python signal handler does not get executed inside the low-level (C) signal handler. Instead, the low-level signal handler sets a flag which tells the virtual machine to execute the corresponding Python signal handler at a later point(for example at the next bytecode instruction). This has consequences:

It makes little sense to catch synchronous errors like SIGFPE or SIGSEGV that are caused by an invalid operation in C code. Python will return from the signal handler to the C code, which is likely to raise the same signal again, causing Python to apparently hang. From Python 3.3 onwards, you can use the faulthandler module to report on synchronous errors.

A long-running calculation implemented purely in C (such as regular expression matching on a large body of text) may run uninterrupted for an arbitrary amount of time, regardless of any signals received. The Python signal handlers will be called when the calculation finishes.

标准库对signal handler的解释大致是说,python中的信号处理函数不会被低级别的信号处理器触发调用。取而代之的是,低级别的信号处理程序会设置一个标志,用来告诉虚拟机在稍后(例如下一个字节代码指令)来执行信号处理函数。这样的结果是:

难以捕获C代码无效操作引起的同步异常,例如SIGFPE、SIGSEGV。Python将从信号处理返回到C代码,这很可能会再次提出同样的信号,导致python挂起。

用C实现的长时计算程序(比如正则表达式匹配大段文本)可能不被中断的运行任意长时间,而不管信号的接收。在计算完成时,python的信号处理函数将被执行。

调用mul_pool.join使得主进程(线程)阻塞在join处,意味着它阻塞在C方法pthread_join调用中。pthread_join并不是一个long-running calculation的程序,而是一个系统调用的阻塞,尽管如此,直到它结束,否则信号处理函数无法被执行。帖子中给出的解决方法时更新python版本至3.3,而我使用的版本是python2.7。这里我并未尝试使用python3.3版本,而是将join用loop sleep代替,简单修改下上面的代码:

1 if __name__ == '__main__':2 print 'current pid is %s' %os.getpid()3 mul_pool =Pool()4 signal.signal(signal.SIGTERM, term)5

6 for i in range(3):7 mul_pool.apply_async(func=fun, args=(str(i),))8

9 whileTrue:10 time.sleep(60)

这样整个进程组仍然能够在收到SIGTERM命令之后退出,而不留下孤儿进程。但是仔细想想我们这样做是不是有些武断,如果一些worker进程在运行一些重要的业务逻辑,强制结束可能会使得数据的丢失,或者一些其他难以恢复的后果,那么有没有更合理的处理方式,使worker进程在处理完本轮数据后,再退出呢?答案同样是肯定的,python标准库中提供了一些进程间同步的工具,这里我们使用Event对象来做同步。首先我们需要通过multiprocessing.Manager类来获取一个Event对象,用Event来控制worker进程的退出,首先修改worker进程的回调函数:

1 deffun(x, event):2 while notevent.is_set():3 print 'process %s running args is %s' %(os.getpid(), x)4 time.sleep(3)5 print 'process %s, call fun finish' % os.getpid()

event对象是用来控制worker进程的,当然代码中的使用只是一个简单的示例,现实情况中worker进程并非一个while这么简单。我们要通过event来控制worker进程的退出,那么可以看到,当event.is_set() == True时,worker会自动退出,那么可以捕获SIGTERM信号,在signal_handler中将event对象进行set:

1 defterminate(pool, event, sig_num, addtion):2 print 'terminate process %d' %os.getpid()3 if notevent.is_set():4 event.set()5

6 pool.close()7 pool.join()8

9 print 'exit...'

在主进程中,首先要创建一个Manager对象,有它来产生Event对象,注意在创建Manager对象后,通过后台ps命令可以看到,此时会多了一个进程,实际上创建Manager对象就会创建一个新的进程,用于数据的同步,我们在signal信号处理函数中实现设置event,并且终止进程池,而signal.signal回调函数只能有两个参数,所以依旧使用partial偏函数进行处理:

1 if __name__ == '__main__':2 print 'current pid is %s' %os.getpid()3 mul_pool =Pool()4 manager =Manager()5 event =manager.Event()6

7 handler =functools.partial(terminate, mul_pool, event)8 signal.signal(signal.SIGTERM, handler)9

10 for i in range(4):11 mul_pool.apply_async(func=fun, args=(str(i), event))12

13 whileTrue:14 time.sleep(60)

运行程序,通过kill命令发送SIGTERM信号,观察到的现象是收到signal信号之后,执行了event.set()方法,worker进程退出,进程池关闭,但是ps之后,发现还有两个进程在运行,通过进程id和strace命令发现一个是主进程,一个是Manager进程同步对象。代码中,主进程最后进入了loop sleep状态,所以当我们收到信号之后,虽然通过event将worker进程和进程池结束,但是主进程的仍然在sleep,所以Manager进程同步对象也为退出。这样我们可以简单修改下代码来处理,可以在terminate方法中添加manager参数,在方法中显示调用manager.shutdown()关闭进程同步对象,然后强制退出,也可以在主进程中同样使用event来代替whlie True循环。这里我们采用第一种方式,简单修改下上面的代码:

1 defterminate(pool, event, manager, sig_num, addtion):2 print 'terminate process %d' %os.getpid()3 if notevent.is_set():4 event.set()5

6 pool.close()7 pool.join()8 manager.shutdown()9 print 'exit ...'

10 os._exit(0)11

12 if __name__ == '__main__':13 print 'current pid is %s' %os.getpid()14 mul_pool =Pool()15 manager =Manager()16 event =manager.Event()17

18 handler =functools.partial(terminate, mul_pool, event, manager)19 signal.signal(signal.SIGTERM, handler)20

21 for i in range(4):22 mul_pool.apply_async(func=fun, args=(str(i), event))23

24 whileTrue:25 time.sleep(60)

python主进程退出时子进程也退出_主进程被杀死时,如何保证子进程同时退出,而不变为孤儿进程(三)...相关推荐

  1. 转:Python 主进程被杀死时,如何保证子进程同时退出而不变为孤儿进程

    发布于博客园,作者:Tourun <主进程被杀死时,如何保证子进程同时退出,而不变为孤儿进程(一)> <主进程被杀死时,如何保证子进程同时退出,而不变为孤儿进程(二)> < ...

  2. 为什么python打开pygame秒关闭后在运行_当我关闭Pygame时屏幕冻结

    我推荐以下代码.首先,它包括时钟,这样你的程序就不会让CPU除了轮询事件什么也不做.其次,它调用pygame.quit(),防止程序在windows上空闲运行时冻结.# Sample Python/P ...

  3. arduino编程时加{}报错_使用Arduino开发板时最常见的10个错误

    作为Arduino开发板的初学者,对于没有电子背景的人来说非常具有挑战性,你会遇到很多错误,其中一些可能有简单的解决方案,但可能需要几天的时间来解决.因此,为了使事情变得更容易,我制作了一个包含10个 ...

  4. Linux中的进程控制:进程退出、孤儿进程、僵尸进程 概念及代码示例 [Linux高并发服务器开发]

    目录 一.进程退出 二.孤儿进程 三.僵尸进程 一.进程退出 #include <stdlib.h> void  exit ( int status ); #include <uni ...

  5. 子进程、僵尸进程、孤儿进程(个人总结)

    声明: 1. 本文为我的个人复习总结, 并非那种从零基础开始普及知识 内容详细全面, 言辞官方的文章               2. 由于是个人总结, 所以用最精简的话语来写文章           ...

  6. vmware-vmx.exe无法结束进程_孤儿进程与僵尸进程产生原理分析,以及终极解决方案案例实现...

    开发中,在io密集型的场景下,我们可以使用多进程(多线程/协成更nber)来提高任务的处理速度.这就需要主进程需要等待所有工作进程执行完毕后才可以去汇总结果后退出. 但如果不规范的编写程序,就可能导致 ...

  7. java 僵尸进程_孤儿进程与僵尸进程

    开发中,在io密集型的场景下,我们可以使用多进程(多线程/协成更nber)来提高任务的处理速度.这就需要主进程需要等待所有工作进程执行完毕后才可以去汇总结果后退出. 但如果不规范的编写程序,就可能导致 ...

  8. Linux进程学习(孤儿进程和守护进程)

    孤儿进程和守护进程 通过前面的学习我们了解了如何通过fork()函数和vfork()函数来创建一个进程.现在 我们继续深入来学习两个特殊的进程:孤儿进程和守护进程 一.孤儿进程 1.什么是 孤儿进程 ...

  9. Z(zombie)僵尸进程和孤儿进程

    僵尸进程 1.什么是僵尸进程? (1) 僵死状态是一个比较特殊的状态,当子进程退出但父进程没有读取到子进程退出返回的代码时就会产生僵死状态,僵死进程会占用你的正常资源 (2)僵死进程会以终止状态保持在 ...

最新文章

  1. 《DSP using MATLAB》Problem 6.3
  2. RDKit | 化合物活性数据的不平衡学习
  3. ef mysql自动更新_EF Core中怎么实现自动更新实体的属性值到数据库
  4. 三步搭建Spring Cloud 服务注册
  5. Bootstrap导航栏
  6. a label can only be part of a statement and a declaration is not a statement
  7. 【ArcGIS遇上Python】ArcGIS批量为多个矢量图层添加一个或多个字段(Add Field)案例实现
  8. Visual Studio 2013 / 2015 完全卸载 - C语言零基础入门教程
  9. 风控策略和模型的区别_风控策略概述
  10. 调用Xmlrpc接口
  11. mysql导出excel出乱码_Mysql中文乱码以及导出为sql语句和Excel问题解决方法[图文]...
  12. sql server 外键_什么是SQL Server中的外键
  13. 程序员的思维修炼9——超越专家
  14. php安装调式redis扩展,下载安装thinkphp5.0,调试Redis是否可以正常使用
  15. Android模拟器 使用 Fiddler抓包
  16. 马科维茨投资组合理论
  17. 深度 | 巨头都在追逐的眼球追踪技术,究竟能带来什么?
  18. Matlab如何修改坐标轴在figure里面的位置
  19. 数字图像处理及应用 阅读笔记
  20. [程序设计]前端Web页面使用原生JavaScript实现校验身份证号码在算法层面是否合法

热门文章

  1. 腾讯Q3财报看点:净利近10年来首次下滑 为硬科技持续“烧钱”
  2. 年仅50岁、千亿身价!贝壳创始人兼董事长左晖因病去世
  3. 闲鱼的真正用法,其实是找对象
  4. 小屏手机大势已去!iPhone 12 Pro Max被买空mini居然还有货
  5. 腾讯旗下微保被罚12万元 相关责任人受到警告处分
  6. 瑞幸咖啡退市成定局:董事长被要求辞职,新店却仍在扩张
  7. 王自如、罗永浩将一起出镜直播带货?罗永浩亲自回应
  8. 因改变,赢未来!三星Galaxy重磅新品年后首发!
  9. 截至11日14时37分,“11.11京东全球好物节”累计下单金额突破1794亿元
  10. 8月29日见!卢伟冰:Redmi首款互联网电视将采用70英寸巨屏