从multiprocessing源码理解其对SIGINT信号的屏蔽

来源:互联网 发布:易语言写软件 编辑:程序博客网 时间:2024/05/17 23:56

问题引入

由于pythonGIL(全局解释器锁,Global Interpreter Lock),GIL在任意时刻只运行单个python线程执行,python的多线程并不能真正的利用多核CPU,无论有多少个核,程序都职能在一个处理器上运行。
要想利用多核并行计算,就必须使用多进程(直白点讲就是同时运行多个python程序)。关于多进程,python自带了multiprocessing。但在使用中发现,常用的Ctrl+C结束python程序的方法对multiprocessing是不适用的。下面就分析一下这个问题背后的原因。

想用Ctrl+C结束一个python程序,有两种方法。

处理KeyboardInterrupt异常

当用户键盘输入Ctrl+C时,python程序会抛出KeyboardInterrupt异常。只需要捕获这个异常时结束程序即可。

import syscount=0try:    while True:        count+=1        print(count)except KeyboardInterrupt as e:    print('end')    sys.exit(0)# 结束进程

处理SIGINT信号

用户按下Ctrl+Clinux内核会向进程发送SIGINT信号,只需在信号处理函数内退出即可。

import signalimport sysdef signal_handler(signal, frame):    #print('end')# 注意,不能在信号处理函数内使用不可重入函数    x = 3    sys.exit(0)signal.signal(signal.SIGINT, signal_handler)count=0while True:    count+=1    print(count)

multiprocessing多进程编程

下面是一个用multiprocessing写的多进程处理程序。

import multiprocessingimport timedef func(msg):    while True:        print('process-{}'.format(msg))        time.sleep(1)if __name__ == "__main__":    P = multiprocessing.Pool(processes=4)# 4 threads    for i in range(10):        P.apply_async(func, (i,))    P.close()    P.join()

为了让该程序能处理Ctrl+C,尝试了上面两种方法。加KeyboardInterrupt异常处理,SIGINT信号处理,发现都不行。这是为什么呢?
Ctrl+C本质上就是让内核发送SIGINT信号,那我们先了解一下linux信号的机制。

linux信号机制

信号是进程间通信的一种机制。OS内核也可以因为内部事件,给进程发送信号(检查进程表中进程的状态,有选择的发送),通知进程发生了某个事件。

multiprocessing源码解释

在上例使用multiprocessing的代码中,我们首先创建进程池P,然后异步执行进程
这两步都做了些什么呢,我们可以到multiprocessing源码中看看。multiprocessingpython自带的库,所以其源码一般位于python安装路径的lib下,如C:\Python27\Lib\multiprocessing。通过源码可以发现:

  • 创建进程池P: 创建了Pool对象。
  • 异步执行进程: 执行Pool对象的成员函数apply_async,返回结果为ApplyResult类型的对象。

ApplyResult类的构造函数__init__中,第一行代码是threading.Condition(threading.Lock())Condition对象初始化时,就是被lock住的,SIGINT信号就没法被lock住后面的代码捕获,所以Ctrl+Cmultiprocessing无效。

结论

multiprocessing创建的每个进程,在进程执行函数结束(返回)之前,都是被lock住的。被lock住的代码没法接收到SIGINT信号。
关于怎么解决这个问题,[3]中有一些workaround。最好的解决办法,应该就是其中提到的用python3concurrent.futures包来做多进程,而不是用multiprocessing

参考

  • [1] python 多线程编程并不能真正利用多核的CPU, http://www.cnblogs.com/cloudaice/archive/2012/01/26/python_thread03.html
  • [2] Linux 信号signal处理机制, http://www.cnblogs.com/taobataoma/archive/2007/08/30/875743.html
  • [3] Solution. https://segmentfault.com/a/1190000004172444
原创粉丝点击