Pythonで次のことを行う必要があります。プロセス (サブプロセス モジュール?) を生成したいのですが、次のようにします。
- プロセスが正常に終了した場合は、終了した瞬間から正確に続行します。
- それ以外の場合、プロセスが「スタック」し、(たとえば) 1 時間以内に終了しない場合は、プロセスを強制終了して続行します (おそらく、ループでもう一度試行します)。
これを達成するための最もエレガントな方法は何ですか?
モジュールはあなたのsubprocess
友達になります。オブジェクトを取得するプロセスを開始し、Popen
次のように関数に渡します。これはタイムアウト時にのみ例外を発生させることに注意してください。必要に応じて、例外をキャッチkill()
し、プロセスでメソッドを呼び出すことができPopen
ます。(kill は Python 2.6 の新機能です)
import time
def wait_timeout(proc, seconds):
"""Wait for a process to finish, or raise exception after timeout"""
start = time.time()
end = start + seconds
interval = min(seconds / 1000.0, .25)
while True:
result = proc.poll()
if result is not None:
return result
if time.time() >= end:
raise RuntimeError("Process timed out")
time.sleep(interval)
プロセスの PID がわかっていれば、psutilを使用してこれを行う方法が少なくとも 2 つあります。プロセスが次のように作成されていると仮定します。
import subprocess
subp = subprocess.Popen(['progname'])
...次のような忙しいループで作成時間を取得できます。
import psutil, time
TIMEOUT = 60 * 60 # 1 hour
p = psutil.Process(subp.pid)
while 1:
if (time.time() - p.create_time()) > TIMEOUT:
p.kill()
raise RuntimeError('timeout')
time.sleep(5)
...または単に、これを行うことができます:
import psutil
p = psutil.Process(subp.pid)
try:
p.wait(timeout=60*60)
except psutil.TimeoutExpired:
p.kill()
raise
また、次の追加の API に興味があるかもしれません。
>>> p.status()
'running'
>>> p.is_running()
True
>>>
同様の質問があり、この回答を見つけました。完全を期すために、一定時間後にハングしているプロセスを終了する方法をもう 1 つ追加したいと思います。Python シグナル ライブラリ https://docs.python.org/2/library/signal.html
ドキュメントから:
import signal, os
def handler(signum, frame):
print 'Signal handler called with signal', signum
raise IOError("Couldn't open device!")
# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)
# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)
signal.alarm(0) # Disable the alarm
とにかく新しいプロセスを生成したかったので、これは問題の最善の解決策ではないかもしれません.
threading.Timer を使用してコールバック関数を設定することも、優れた受動的な方法です。
from threading import Timer
# execute the command
p = subprocess.Popen(command)
# save the proc object - either if you make this onto class (like the example), or 'p' can be global
self.p == p
# config and init timer
# kill_proc is a callback function which can also be added onto class or simply a global
t = Timer(seconds, self.kill_proc)
# start timer
t.start()
# wait for the test process to return
rcode = p.wait()
t.cancel()
プロセスが時間内に終了し、wait() が終了してコードがここで続行される場合、cancel() はタイマーを停止します。その間にタイマーが切れて別のスレッドで kill_proc を実行すると、wait() もここで続行され、cancel() は何もしません。rcode の値によって、タイムアウトしたかどうかがわかります。最も単純な kill_proc : (もちろん、そこで追加の処理を実行できます)
def kill_proc(self):
os.kill(self.p, signal.SIGTERM)