【问题标题】:Terminating a subprocess with KeyboardInterrupt使用 KeyboardInterrupt 终止子进程
【发布时间】:2017-01-22 19:43:19
【问题描述】:

我正在使用 Python 调用使用 subprocess 模块的 C++ 程序。由于程序需要一些时间来运行,我希望能够使用 Ctrl+C 来终止它。我在 StackOverflow 上看到了一些与此相关的问题,但似乎没有一个解决方案适合我。

我希望子进程在 KeyboardInterrupt 上终止。这是我的代码(类似于其他问题中的建议):

import subprocess

binary_path = '/path/to/binary'
args = 'arguments' # arbitrary

call_str = '{} {}'.format(binary_path, args)

proc = subprocess.Popen(call_str)

try:
    proc.wait()
except KeyboardInterrupt:
    proc.terminate()

但是,如果我运行它,代码会挂起,等待进程结束,并且永远不会注册 KeyboardInterrupt。我也尝试了以下方法:

import subprocess
import time

binary_path = '/path/to/binary'
args = 'arguments' # arbitrary

call_str = '{} {}'.format(binary_path, args)

proc = subprocess.Popen(call_str)
time.sleep(5)
proc.terminate()

这段代码 sn-p 可以很好地终止程序,所以问题不是发送到终止的实际信号。

如何更改代码以在 KeyboardInterrupt 上终止子进程?

我正在运行 Python 2.7 和 Windows 7 64 位。提前致谢!

我尝试过的一些相关问题:

Python sub process Ctrl+C

Kill subprocess.call after KeyboardInterrupt

kill subprocess when python process is killed?

【问题讨论】:

  • 为什么不在主程序中捕获 ctrl-c 信号并使用它来调用 proc.terminate()
  • 在 Python 3 中,您可以使用 _winapi.WaitForMultipleObjects([proc._handle], False, -1)。对于主线程,如果 wait-all 标志为 false,此等待会自动包含 Python 的 SIGINT 事件。在 2.x 中,您必须使用 ctypes 或 PyWin32 从头开始​​实现。
  • @eryksun 感谢您的建议。有一个名为 win32event 的模块具有类似的功能,可以为 Python 2 导入,但我尝试了win32event.WaitForMultipleObjects([proc._handle], False, -1) 并没有什么不同。
  • @limi44, 3.x 使用由 Ctrl+C 的信号处理程序设置的 Windows 事件对象。调用 _winapi.WaitForMultipleObjects 时,此事件会自动添加到列表中。在 2.x 中实现这一点需要 PyWin32 或 ctypes 定义一个控制台 CTRL_C_EVENT 处理程序(例如,将 ctypes 回调函数传递给 SetConsoleCtrlHandler),它设置一个事件(通过 CreateEvent 创建)。然后包装WaitForMultipleObjects 以将Ctrl+C 事件句柄附加到列表并在等待之前重置事件(ResetEvent),但仅在从主线程调用时。

标签: python windows subprocess interrupt terminate


【解决方案1】:

我想出了一种方法来做到这一点,类似于让-弗朗索瓦的回答,但没有多线程。关键是使用 Popen.poll() 来确定子进程是否已完成(如果仍在运行将返回 None )。

import subprocess
import time

binary_path = '/path/to/binary'
args = 'arguments' # arbitrary

call_str = '{} {}'.format(binary_path, args)

proc = subprocess.Popen(call_str)

try:
    while proc.poll() is None:
        time.sleep(0.1)

except KeyboardInterrupt:
    proc.terminate()
    raise

我在 KeyboardInterrupt 之后添加了一个额外的 raise,因此 Python 程序除了子进程之外也被中断。

编辑:根据 eryksun 的评论将 pass 更改为 time.sleep(0.1) 以减少 CPU 消耗。

【讨论】:

  • time.sleep(.1) 是可中断的。使用它而不是 pass 应该可以减少 CPU 消耗。
【解决方案2】:

我在 Windows 上的丑陋但成功的尝试:

import subprocess
import threading

import time

binary_path = 'notepad'
args = 'foo.txt' # arbitrary

proc = None
done = False

def s():
    call_str = '{} {}'.format(binary_path, args)
    global done
    global proc
    proc = subprocess.Popen(call_str,stdout=subprocess.PIPE)
    proc.wait()
    done = True


t = threading.Thread(target=s)
t.start()


try:
    while not done:
        time.sleep(0.1)

except KeyboardInterrupt:
    print("terminated")
    proc.terminate()

创建一个子进程运行的线程。导出proc 变量。

然后在非活动循环中永远等待。当按下 CTRL+C 时,将触发异常。进程间通信(例如:proc.wait())与 CTRL+C 处理冲突,否则。在线程中运行时,没有这样的问题。

注意:我曾尝试使用 threading.lock() 来避免这个时间循环,但偶然发现了相同的 CTRL+C 忽略。

【讨论】:

    猜你喜欢
    • 2018-10-30
    • 2011-01-21
    • 1970-01-01
    • 1970-01-01
    • 2023-03-07
    • 2016-04-12
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    相关资源
    最近更新 更多