2016-09-14 56 views
0

我正在使用Python來使用子進程模塊調用C++程序。由於該程序需要一段時間才能運行,我希望能夠使用Ctrl + C來終止它。我在StackOverflow上看到了一些關於這個問題的問題,但是沒有一個解決方案似乎適用於我。使用KeyboardInterrupt終止子進程

我想要的是子進程在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() 

這段代碼工作在終止程序很好,所以它不是會被髮送到終止,這是問題的實際信號。

如何更改代碼以便子進程可以在KeyboardInterrupt上終止?

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

,我嘗試了一些相關的問題:在Windows

Python sub process Ctrl+C

Kill subprocess.call after KeyboardInterrupt

kill subprocess when python process is killed?

+0

爲什麼不抓住主程序中的ctrl-c信號並使用它來調用'proc.terminate()'? – CoconutBandit

+0

在Python 3中,您可以使用'_winapi.WaitForMultipleObjects([proc._handle],False,-1)'。對於主線程,如果wait-all標誌爲false,則該等待自動包含Python的'SIGINT'事件。在2.x中,你必須從頭開始使用ctypes或PyWin32來實現它。 – eryksun

+0

@eryksun感謝您的建議。有一個名爲win32event的模塊,它具有類似的功能,可以爲Python 2導入,但我嘗試過'win32event.WaitForMultipleObjects([proc._handle],False,-1)',它沒有什麼區別。 – limi44

回答

2

我想出了一種方法來做到這一點,類似於Jean-Francois的循環回答,但沒有多個線程。關鍵是使用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後添加了一個額外的提升,所以Python程序除了子進程還被中斷。

編輯:根據eryksun的評論改變傳遞到time.sleep(0.1)以減少CPU消耗。

+1

'time.sleep(.1)'是可中斷的。使用它代替'pass'應該可以減少CPU消耗。 – eryksun

1

我的醜,但全成嘗試:

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() 

克雷亞te子進程運行的線程。導出變量proc

然後永遠等待一個非活動循環。按下CTRL + C時,會觸發異常。進程間通信(例如:proc.wait())與另外的CTRL + C處理衝突。在線程中運行時,不需要這樣的問題。

注:我試圖避免使用threading.lock()這個時間循環,但偶然發現相同的CTRL + C忽略。