pythonサブプロセスPopenで開始されたプロセスを強制終了するときにstdout-pipeを閉じるにはどうすればよいですか?
-
06-07-2019 - |
質問
別のスレッドで開始されたサブプロセスを強制終了するときに通信パイプをシャットダウンすることは可能かと思います。 communication()を呼び出さない場合、kill()は期待どおりに機能し、5秒ではなく1秒後にプロセスを終了します。
同様の問題こちらの議論を見つけましたが、本当の答えは得られませんでした。パイプを閉じるか、サブサブプロセス(この例では「スリープ」)を明示的に強制終了し、それを強制終了してパイプのブロックを解除する必要があると思います。
SOで彼女の答えも見つけようとしましたが、しか見つかりませんでしたthis および this およびthis、これは私が知る限りこの問題に直接対処していません( ?)。
したがって、私がやりたいのは、2番目のスレッドでコマンドを実行してすべての出力を取得できるようにすることですが、必要に応じて即座に強制終了することです。ファイルを経由してテールまたはそれと同様のものをテールにすることができますが、これを行うためのより良い方法があるはずだと思いますか?
import subprocess, time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(args, shell=False, stdout=subprocess.PIPE)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
process.kill()
これはスクリプトです:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "slept five"
出力
$ time python poc.py
process: sleeping five
real 0m5.053s
user 0m0.044s
sys 0m0.000s
解決
問題は、process.kill()が、bashスクリプトのサブプロセスではなく、直接の子プロセス(bash)のみを強制終了することだと思います。
問題と解決策は次のとおりです。
- http://www.doughellmann.com/PyMOTW/subprocess/ #process-groups-sessions
- シェルで起動されたpythonサブプロセスを終了する方法= True
Popen(...、preexec_fn = os.setsid)を使用してプロセスグループを作成し、os.pgkillを使用してプロセスグループ全体を強制終了します。例
import os
import signal
import subprocess
import time
from threading import Thread
process = None
def executeCommand(command, runCommand):
Thread(target=runCommand, args=(command,)).start()
def runCommand(command):
global process
args = command.strip().split()
process = subprocess.Popen(
args, shell=False, stdout=subprocess.PIPE, preexec_fn=os.setsid)
for line in process.communicate():
if line:
print "process:", line,
if __name__ == '__main__':
executeCommand("./ascript.sh", runCommand)
time.sleep(1)
os.killpg(process.pid, signal.SIGKILL)
$ time python poc.py
process: sleeping five
real 0m1.051s
user 0m0.032s
sys 0m0.020s
他のヒント
これを実行してマルチスレッドの問題を回避する最も簡単な方法は、メインスレッドからキルフラグを設定し、通信の直前にスクリプト実行スレッドでそれを確認し、フラグは True
です。
あなたはPythonの非常に粗い並行性の犠牲になっているようです。スクリプトをこれに変更します:
#!/bin/bash
echo "sleeping five"
sleep 5
echo "sleeping five again"
sleep 5
echo "slept five"
そして、出力は次のようになります:
process: sleeping five
real 0m5.134s
user 0m0.000s
sys 0m0.010s
スクリプト全体が実行された場合、時間は10秒になります。そのため、bashスクリプトがスリープするまで、Python制御スレッドは実際には実行されないようです。同様に、スクリプトを次のように変更した場合:
#!/bin/bash
echo "sleeping five"
sleep 1
sleep 1
sleep 1
sleep 1
sleep 1
echo "slept five"
その後、出力は次のようになります。
process: sleeping five
real 0m1.150s
user 0m0.010s
sys 0m0.020s
要するに、あなたのコードは論理的に実装されたように動作します。 :)