我正在使用多进程来生成一个可以停止的任务(multiprocessing.Process
)(不需要任务本身的合作,例如:不使用multiprocessing.Event
之类的东西来通知任务优雅地停止)。
由于.terminate()
(或.kill()
)不会干净地停止它(finally:
子句不会执行),我想我会使用os.kill()
来模拟CTRL+C事件:
from multiprocessing import Process
from time import sleep
import os
import signal
def task(n):
try:
for i in range(n):
sleep(1)
print(f'task: i={i}')
finally:
print('task: finally clause executed!')
return i
if __name__ == '__main__':
t = Process(target=task, args=(10,))
print(f'main: starting task...')
t.start()
sleep(5)
for i in ('CTRL_C_EVENT', 'SIGINT'):
if hasattr(signal, i):
sig = getattr(signal, i)
break
print(f'main: attempt to stop task...')
os.kill(t.pid, sig)
finally:
子句在Windows, macOS和Linux上同时执行;但是在Windows上,它会显示错误:
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
File "c:Python38libmultiprocessingutil.py", line 357, in
_exit_function
p.join()
File "c:Python38libmultiprocessingprocess.py", line 149, in join
res = self._popen.wait(timeout)
File "c:Python38libmultiprocessingpopen_spawn_win32.py", line 108, in wait
res = _winapi.WaitForSingleObject(int(self._handle), msecs)
KeyboardInterrupt
而在macOS和Linux上,它只打印应该是print
ed的消息。
似乎Windows中的CTRL_C_EVENT
也从子进程传播到父进程。请看下面这个相关的问题。
我在代码中添加了一些簿记代码和try...except
块。它显示了发生了什么,并且KeyboardInterrupt
也需要被父进程捕获。
from multiprocessing import Process
from time import sleep
import os
import signal
def task(n):
try:
for i in range(n):
sleep(1)
print(f'task: i={i}')
except KeyboardInterrupt:
print("task: caught KeyboardInterrupt")
finally:
print('task: finally clause executed!')
return i
if __name__ == '__main__':
try:
t = Process(target=task, args=(10,))
print(f'main: starting task...')
t.start()
sleep(5)
for i in ('CTRL_C_EVENT', 'SIGINT'):
if hasattr(signal, i):
sig = getattr(signal, i)
break
print(f'main: attempt to stop task...')
os.kill(t.pid, sig)
finally:
try:
print("main: finally in main process. Waiting for 3 seconds")
sleep(3)
except KeyboardInterrupt:
print("main: caught KeyboardInterrupt in finally block")
它防止错误并产生以下输出:
main: starting task...
task: i=0
task: i=1
task: i=2
task: i=3
main: attempt to stop task...
main: finally in main process. Waiting for 3 seconds
task: caught KeyboardInterrupt
main: caught KeyboardInterrupt in finally block
task: finally clause executed!