【发布时间】:2022-01-24 00:44:36
【问题描述】:
我正在尝试找到一种方法来很好地处理 SIGTERM,并在主进程收到 SIGTERM 时终止我的子进程。
基本上,我是手动创建进程(但我认为问题与mp.pool 相同)
import multiprocessing as mp
...
workers = [
mp.Process(
target=worker,
args=(...,)
) for _ in range(nb_workers)
]
我正在捕捉信号
signal.signal(signal.SIGTERM, term)
signal.signal(signal.SIGINT, term)
signal.signal(signal.SIGQUIT, term)
signal.signal(signal.SIGABRT, term)
当信号被捕获时,我想终止我的所有子进程并退出。我不想等待他们完成运行,因为他们的个人运行时间可能会很长(理解几分钟)。
同样,我不能真正设置threading.Event() 所有进程都会定期查看,因为它们基本上只是在做一个巨大但缓慢的操作(取决于几个库)。
我的想法是在捕获信号时设置一个标志,然后在设置标志时让一个看门狗终止所有子进程。但是使用.terminate() 也会使用SIGTERM,它会被我的信号处理程序再次捕获。
例如,简化代码:
import multiprocessing as mp
import signal
import time
FLAG = False
def f(x):
time.sleep(5)
print(x)
return x * x
def term(signum, frame):
print(f'Received Signal {signum}')
global FLAG
FLAG = True
def terminate(w):
for process in w:
print('Terminating worker {}'.format(process.pid))
process.terminate()
process.join()
process.close()
signal.signal(signal.SIGTERM, term)
signal.signal(signal.SIGINT, term)
signal.signal(signal.SIGQUIT, term)
signal.signal(signal.SIGABRT, term)
if __name__ == '__main__':
workers = [
mp.Process(
target=f,
args=(i,)
) for i in range(4)
]
for process in workers:
process.start()
while not FLAG:
time.sleep(0.1)
print('flag set')
terminate(workers)
print('Done')
如果我在进程完成之前中断代码(使用 ctrl-c):
Received Signal 2
Received Signal 2
Received Signal 2
Received Signal 2
Received Signal 2
flag set
Terminating worker 27742
Received Signal 15
0
Terminating worker 27743
Received Signal 15
1
3
2
Terminating worker 27744
Terminating worker 27745
Done
如您所见,.terminate() 似乎没有终止子进程,因为它们一直运行到最后,而且看起来我们也捕获了生成的 SIGTERM (15)。
到目前为止,我的解决方案是:
- 不知何故设法让进程定期检查
threading.Event()。这意味着要彻底重新思考我们当前的流程在做什么。 - 使用
.kill()而不是.terminate()。这适用于 Linux,但它是一个不太干净的出口。不确定 Windows,但我的印象是在 Windows 上 .kill == .terminate。 - 不要再捕获 SIGTERM,假设程序永远不会以这种方式被杀死(不太可能)
有什么干净的方法来处理这个问题吗?
【问题讨论】:
标签: python multiprocessing signals