在多处理(multiprocessing)的 Python 应用程序中,为了干净地退出并释放资源,通常需要采取以下几种策略。
1、问题背景
当使用多处理的Python脚本时,若是收到 Ctrl-C 信号,通常难以干净地停止该脚本。需要多次按下 Ctrl-C 才能停止,并且屏幕上会出现各种错误消息。
我们如何编写一个在收到 Ctrl-C 信号后能够干净地退出的 Python 脚本?
下面是一个使用多处理的脚本示例:
import numpy as np, time
from multiprocessing import Pool
def countconvolve(N):
np.random.seed() # ensure seed is random
count = 0
iters = 1000000 # 1million
l=12
k=12
l0=l+k-1
for n in range(N):
t = np.random.choice(np.array([-1,1], dtype=np.int8), size=l0 * iters)
v = np.random.choice(np.array([-1,1], dtype=np.int8), size = l * iters)
for i in xrange(iters):
if (not np.convolve(v[(l*i):(l*(i+1))],
t[(l0*i):(l0*(i+1))], 'valid').any()):
count += 1
return count
if __name__ == '__main__':
start = time.clock()
num_processes = 8
N = 13
pool = Pool(processes=num_processes)
res = pool.map(countconvolve, [N] * num_processes)
print res, sum(res)
print (time.clock() - start)
2、解决方案
封装 Pool 对象
一种解决方案是将 Pool 对象封装在一个类中,该类可以处理 Ctrl-C 信号并干净地关闭池。
import numpy as np, time
from multiprocessing import Pool
class WorkerPool(Pool):
def __init__(self, processes=8):
super().__init__(processes=processes)
self._closed = False
def close(self):
if not self._closed:
super().close()
self._closed = True
def terminate(self):
if not self._closed:
super().terminate()
self._closed = True
def join(self):
if not self._closed:
super().join()
self._closed = True
if __name__ == '__main__':
start = time.clock()
num_processes = 8
N = 13
pool = WorkerPool(processes=num_processes)
try:
res = pool.map(countconvolve, [N] * num_processes)
print res, sum(res)
print (time.clock() - start)
except KeyboardInterrupt as e:
print 'Stopping..'
pool.close()
pool.join()
使用 WorkerPool 类,就可以在收到 Ctrl-C 信号时,使用 close() 和 join() 方法来干净地关闭池。
使用信号处理程序
另一种解决方案是使用信号处理程序来处理 Ctrl-C 信号。可以将信号处理程序注册到 SIGINT 信号,当收到该信号时,就会调用信号处理程序。
import numpy as np, time
from multiprocessing import Pool
import signal
# 定义一个全局的池变量
pool = None
def term_signal_handler(signum, frame):
global pool
print 'CTRL-C pressed'
try:
pool.close()
pool.join()
except AttributeError:
print 'Pool has been already closed'
def countconvolve(N):
np.random.seed() # ensure seed is random
count = 0
iters = 1000000 # 1million
l=12
k=12
l0=l+k-1
for n in range(N):
t = np.random.choice(np.array([-1,1], dtype=np.int8), size=l0 * iters)
v = np.random.choice(np.array([-1,1], dtype=np.int8), size = l * iters)
for i in xrange(iters):
if (not np.convolve(v[(l*i):(l*(i+1))],t[(l0*i):(l0*(i+1))], 'valid').any()):
count += 1
return count
if __name__ == '__main__':
# 注册信号处理程序
signal.signal(signal.SIGINT, term_signal_handler)
start = time.clock()
num_processes = 8
N = 13
pool = Pool(processes=num_processes)
try:
res = pool.map(countconvolve, [N] * num_processes)
print res, sum(res)
print (time.clock() - start)
except KeyboardInterrupt as e:
print 'Stopping..'
在该脚本中,使用 signal.signal() 函数将 term_signal_handler() 函数注册到 SIGINT 信号。当收到 SIGINT 信号时,就会调用 term_signal_handler() 函数,该函数会关闭并加入池,从而干净地退出脚本。
根据具体需求选择适合的退出方式,可以让多处理程序更加稳定和优雅。