Python版本: 3.9.8
信号模块: signal
多进程模块: multiprocess
1. 主进程接收信号,子进程发送信号。
子进程在结束时会自动给主进程发送SIGCHLD
信号[1],因此在主进程内配置相应的处理函数即可捕获子进程的信号。
如下:
import multiprocessing
import signal
def handler():
print("sub process doing")
exit(0)
def signal_handler(signum, frame):
print("main process got a signal")
print(signum)
def main():
signal.signal(signal.SIGCHLD, signal_handler) # 设置信号处理函数
p = multiprocessing.Process(target=handler, name='sub process')
p.start()
signal.pause() # 主进程阻塞式等待信号
if __name__ == '__main__':
main()
输出为:
sub process doing
main process got a signal
17
上述示例中,如果存在多个子进程时。主进程是无法区分信号SIGCHLD
由哪个子进程发送的。此时需要在主进程内使用阻塞式的信号等待方法signal.sigwaitinfo
[2],其返回一个信号的结构体,其中包含的si_pid
就是相应的子进程ID。
如下:
import multiprocessing
import signal
import time
import os
def handler():
print(f"sub process: {os.getpid()} doing")
time.sleep(1)
exit(0)
def signal_handler(signum, frame):
print(f"main process got a signal: {signum}")
def main():
signal.signal(signal.SIGCHLD, signal_handler) # 设置信号处理函数
for _ in range(2):
p = multiprocessing.Process(target=handler, name='sub process')
p.start()
#signal.pause() # 主进程阻塞式等待信号
while True:
s = signal.sigwaitinfo([signal.SIGCHLD]) # 不会再调用signal_handler()
signal_handler(s.si_signo, None) # 改为手动调用
print(f"sub process pid: {s.si_pid}")
if __name__ == '__main__':
main()
输出为:
sub process: 302537 doing
sub process: 302538 doing
main process got a signal: 17
sub process pid: 302537
main process got a signal: 17
sub process pid: 302538
2. 主进程发送信号,子进程接收信号。
在子进程内绑定相应的信号处理程序,之后由信号处理程序控制一个开关变量,进而达到控制子进程的主循环逻辑。
如下:
import multiprocessing
import time
import os
import signal
import logging
logger = logging.getLogger(__name__)
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
logger.addHandler(handler)
def print(info):
logger.debug(info)
class SubProcess(object):
flag = True
def signal_handler(self, signum, frame):
print("signal handler doing")
print(signum)
self.flag = False
def handler(self):
signal.signal(signal.SIGINT, self.signal_handler) # 绑定信号处理函数
print("handler doing")
while True:
if self.flag is False:
print("sub process flag is False")
break
time.sleep(3)
exit(0)
def main():
sp = SubProcess()
p = multiprocessing.Process(target=sp.handler, name='sub_ps')
p.start()
while True:
time.sleep(5)
os.kill(p.pid, signal.SIGINT) # send SIGINT every 5 seconds
print("main looping")
if __name__ == '__main__':
main()
输出结果:
2022-09-19 15:51:55,395 - __main__ - DEBUG - handler doing
2022-09-19 15:52:00,399 - __main__ - DEBUG - signal handler doing
2022-09-19 15:52:00,399 - __main__ - DEBUG - 2
2022-09-19 15:52:00,399 - __main__ - DEBUG - main looping
2022-09-19 15:52:01,399 - __main__ - DEBUG - sub process flag is False
2022-09-19 15:52:05,405 - __main__ - DEBUG - main looping
2022-09-19 15:52:10,410 - __main__ - DEBUG - main looping
2022-09-19 15:52:15,415 - __main__ - DEBUG - main looping
3. 总结
在主进程无阻塞式任务的前提下,可结合上述两种情况,通过信号来完成多进程之间的控制。
参考:
[1]. 孙剑.Linux系统编程[M].人民邮电出版社. 26.3章节.SIGCHLD信号
[2]. https://docs.python.org/3/library/signal.html#signal.sigwaitinfo