• multiprocessing 让子进程忽略信号,手动关闭子进程


    起因

    同事想要写一个代码,主进程中监听SIGINT、SIGTERM信号退出,并关闭启动的子进程,代码类似这样

    import signal
    import sys
    import time
    from multiprocessing import Process
    from multiprocessing import Manager
    
    
    class Test:
    
        def __init__(self):
            self.is_running = True
            if sys.platform != 'win32':
                signal.signal(signal.SIGHUP, self._signal_handler)
            signal.signal(signal.SIGINT, self._signal_handler)
            signal.signal(signal.SIGTERM, self._signal_handler)
    
        def _signal_handler(self, signum, frame):
            """
            Terminate scenario ticking when receiving a signal interrupt
            """
            self.is_running = False
            print('关闭场景调度')
    
        def listen_manage(self, running, kill_task_lst):
            """监控取消任务"""
            while running.value:
                kill_task_lst.append(1)
                time.sleep(1)
                print("child is running")
    
        def run(self):
            """启动场景调度"""
            kill_task_lst = Manager().list()
            running = Manager().Value(bool, True)
            print('启动监控进程')
            p = Process(target=self.listen_manage, args=(running, kill_task_lst, ))
            p.start()
            while self.is_running:
                time.sleep(0.5)
                print("main is running")
    

    但是发现在ctrl + c 时候,子进程也同时接收到了信号,退出了

    启动监控进程
    main is running
    main is running
    child is running
    main is running
    关闭场景调度
    Process Process-3:
    Traceback (most recent call last):
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\process.py", line 315, in _bootstrap
        self.run()
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\process.py", line 108, in run
        self._target(*self._args, **self._kwargs)
      File "D:\python_projects\pythonProject\test_egg\test.py", line 35, in listen_manage
        time.sleep(1)
    KeyboardInterrupt
    main is running
    

    这时猜想到可能是子进程也监听了SIGINT,退出了程序,所以想在子任务中设置忽略信号,改造后的 listen_manage:

        def listen_manage(self, running, kill_task_lst):
            """监控取消任务"""
            if sys.platform != 'win32':
                signal.signal(signal.SIGHUP, signal.SIG_IGN)
            signal.signal(signal.SIGINT, signal.SIG_IGN)
            signal.signal(signal.SIGTERM, signal.SIG_IGN)
            while running.value::
                kill_task_lst.append(1)
                time.sleep(1)
                print("child is running")
    

    运行后会报 BrokenPipeError: [WinError 232] 管道正在被关闭。

    启动监控进程
    main is running
    main is running
    child is running
    main is running
    关闭场景调度
    main is running
    child is running
    Process Process-4:
    Traceback (most recent call last):
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\process.py", line 315, in _bootstrap
        self.run()
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\process.py", line 108, in run
        self._target(*self._args, **self._kwargs)
      File "D:\python_projects\pythonProject\test_egg\test.py", line 37, in listen_manage
        kill_task_lst.append(1)
      File "", line 2, in append
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\managers.py", line 834, in _callmethod
        conn.send((self._id, methodname, args, kwds))
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\connection.py", line 206, in send
        self._send_bytes(_ForkingPickler.dumps(obj))
      File "D:\software\anaconda\envs\test_egg1\lib\multiprocessing\connection.py", line 280, in _send_bytes
        ov, err = _winapi.WriteFile(self._handle, buf, overlapped=True)
    BrokenPipeError: [WinError 232] 管道正在被关闭。
    

    这里想到的原因是父进程直接退出了,导致共享变量kill_task_lst被关闭导致的,所以想在关闭管道前等待子进程退出
    可执行的完整代码:

    import signal
    import sys
    import time
    from multiprocessing import Process
    from multiprocessing import Manager
    
    
    class Test:
    
        def __init__(self):
            self.is_running = True
            if sys.platform != 'win32':
                signal.signal(signal.SIGHUP, self._signal_handler)
            signal.signal(signal.SIGINT, self._signal_handler)
            signal.signal(signal.SIGTERM, self._signal_handler)
    
        def _signal_handler(self, signum, frame):
            """
            Terminate scenario ticking when receiving a signal interrupt
            """
            self.is_running = False
            print('关闭场景调度')
    
        def listen_manage(self, running, kill_task_lst):
            """监控取消任务"""
            if sys.platform != 'win32':
                signal.signal(signal.SIGHUP, signal.SIG_IGN)
            signal.signal(signal.SIGINT, signal.SIG_IGN)
            signal.signal(signal.SIGTERM, signal.SIG_IGN)
            while running.value:
                kill_task_lst.append(1)
                time.sleep(1)
                print("child is running")
    
        def run(self):
            """启动场景调度"""
            kill_task_lst = Manager().list()
            running = Manager().Value(bool, True)
            print('启动监控进程')
            p = Process(target=self.listen_manage, args=(running, kill_task_lst, ))
            p.start()
            while self.is_running:
                time.sleep(0.5)
                print("main is running")
            running.set(False)
            while p.is_alive():
                time.sleep(0.01)
            print("finish.....")
    
    if __name__ == '__main__':
        t = Test()
        t.run()
    

    执行结果:

    启动监控进程
    main is running
    main is running
    child is running
    main is running
    关闭场景调度
    main is running
    child is running
    finish.....
    
  • 相关阅读:
    【接口测试】工具篇Postman
    Flutter实现地图上汇聚到一点的效果。
    J2EE项目部署与发布(Windows版本)
    计算机网络——网络层の选择题整理
    Linux安装redis详细教程
    我在Blue Nile(蓝色尼罗河)上通过python爬取一百万颗钻石,最终选出心仪的一颗
    怎样选择合适的CRM客户管理系统?
    使用JMeter的JSON提取器:通过递归下降查找,从接口响应中提取特定字段
    Ubuntu使用过程中的常见问题及解决方案
    cmd命令快速打开MATLAB
  • 原文地址:https://www.cnblogs.com/luslin/p/16658272.html