pythonサブプロセスPopenで開始されたプロセスを強制終了するときにstdout-pipeを閉じるにはどうすればよいですか?

StackOverflow https://stackoverflow.com/questions/1624254

質問

別のスレッドで開始されたサブプロセスを強制終了するときに通信パイプをシャットダウンすることは可能かと思います。 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)のみを強制終了することだと思います。

問題と解決策は次のとおりです。

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

要するに、あなたのコードは論理的に実装されたように動作します。 :)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top