【问题标题】:interrupt python multiprocessing.Process using signals in Windows在 Windows 中使用信号中断 python multiprocessing.Process
【发布时间】:2017-03-09 01:59:57
【问题描述】:

我有一个 python 脚本,它使用 multiprocessing.Process 类生成一个新进程。这个过程应该永远运行以监控东西。在 Unix 上,我现在可以使用 os.kill() 向该特定进程发送信号,并在该进程中使用 signal.signal(...) 来实现我的特定中断处理程序。在 Windows 上无法正常工作。

我阅读了使用popen 的方法。我也可以为 Process 类指定 CREATE_NEW_PROCESS_GROUP 标志吗?以及如何?

这是我的示例代码:

import multiprocessing as mp
import time
import signal
import os
import platform

def my_h(signal, frame):
    print("recieved signal", signal)
    raise InterruptedError

def a_task():
    signal.signal(signal.SIGINT, my_h)
    print("this is 'a_task'", os.getpid())
    try:
        while True:
            print(time.time())
            time.sleep(1)
    except Exception as e:
        print(type(e), e)
    print("'a_task' is at end")


if __name__ == '__main__':
    p = mp.Process(target=a_task)
    p.start()
    time.sleep(1)

    if platform.system() == 'Windows':
        print("send CTRL_C_EVENT")
        os.kill(p.pid, signal.CTRL_C_EVENT)
    elif platform.system() == 'Linux':
        print("send SIGINT")
        os.kill(p.pid, signal.SIGINT)

    time.sleep(3)
    try:
        os.kill(p.pid, signal.SIGTERM)
    except:
        pass

【问题讨论】:

  • 我认为其他解决方案可以解决您的问题。 stackoverflow.com/questions/1230669/…
  • ẁell 这不仅仅是终止进程,而是确保在停止之前执行except 子句
  • 多处理是自包含的,并没有提供太多的钩子让您修改它的行为方式。 _winapi.CreateProcess 调用是硬编码的。我不建议对其进行猴子修补——尤其是对于像这样可疑的东西。 Windows 不实现信号。 CTRL_C_EVENTCTRL_BREAK_EVENT 是控制台控制事件,因此仅在通过 python.exe 或附加到 conhost.exe 实例的类似控制台模式可执行文件运行时才有效。它不能通过 pythonw.exe、另一个 GUI 脚本包装器或作为服务工作(服务可执行文件是独立运行的)。

标签: python windows signals python-multiprocessing


【解决方案1】:

我找到了一种解决方法,使用multiprocessing.Event 类实现信号。

然后线索是找到在主线程中引发 KeybordInterrupt 的 interrupt_main() 方法(在 thread (Python2) 或 _thread (Python3) 中),这是我想要中断的进程。

import multiprocessing as mp
import time
import signal
import os
import threading
import _thread

def interrupt_handler(interrupt_event):
    print("before wait")
    interrupt_event.wait()
    print("after wait")
    _thread.interrupt_main()

def a_task(interrupt_event, *args):
    task = threading.Thread(target=interrupt_handler, args=(interrupt_event,))
    task.start()

    print("this is 'a_task'", os.getpid())
    try:
        while True:
            print(time.time())
            time.sleep(1)
    except KeyboardInterrupt:
        print("got KeyboardInterrupt")
    print("'a_task' is at end")


if __name__ == '__main__':
    interrupt_event = mp.Event()
    p = mp.Process(target=a_task, args = (interrupt_event, tuple()))
    p.start()
    time.sleep(2)
    print("set interrupt_event")
    interrupt_event.set()

    time.sleep(3)
    try:
        os.kill(p.pid, signal.SIGTERM)
    except:
        pass

【讨论】:

  • 这可行,但通常每个进程中的主线程会等待或定期轮询事件并设置一些信号让工作线程正常退出,这可以像全局布尔值一样简单。跨度>
  • 嗯,这也是我的第一个想法。但是想想一项在相当长一段时间内确实需要处理数字的任务。如果我想中断该进程,那将意味着中断工作线程(非主线程)。当工人没有反应时,我没有找到办法。在 Unix 上,我发送 SIGINT 并用 try except 捕获它。上面的解决方案有点类似于 Unix 的工作流程,这就是我使用它的原因。但无论如何,对于 cmets。
  • 我预计严重的数字运算将被卸载到 C 扩展,在这方面,大多数内置函数对被中断的支持很糟糕甚至不支持。他们释放 GIL 并去做自己的事情。如果它是主线程,那么你会被淹没,因为 Python 会在其上处理信号——因此interrupt_main 函数会在解释器在主线程上运行时为其设置一个标志。
  • 好吧,这个例子可能因为它的简单性而具有误导性。映像在具有多个内核的某些机器上运行的客户端应用程序(python 脚本)。客户端的主线程现在启动多个进程以使用多个内核进行数字运算。每个子进程(从某个服务器应用程序)获取数据。现在假设用户决定退出客户端,然后每个子进程都应该向服务器发送一条消息,表明计算不成功。所以客户端的中断应该中断每个子进程,让每个子进程做一些清理工作。
  • 我只是想说明这不是灵丹妙药。工作进程的主线程可能已经释放了 GIL,并且正在 Fortran 扩展函数中进行一些 5 分钟的数字运算任务。您将无法通过interrupt_main 中断它。这只是为解释器设置了一个标志。举个简单的例子,在 Linux 上的 Python 3 中试试这个:sum(range(2**28))。你不能通过 Ctrl+C 打断它。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-17
  • 1970-01-01
  • 2012-08-30
相关资源
最近更新 更多