2009-10-26 10 views
9

Ich frage mich, ob es möglich ist, die Kommunikationsleitung herunterzufahren, wenn Sie einen Subprozess beenden, der in einem anderen Thread gestartet wurde. Wenn ich communicate() nicht anrufe, funktioniert kill() wie erwartet und beendet den Prozess nach einer Sekunde statt nach fünf.Wie schließe ich die Stdout-Pipe, wenn Sie einen Prozess beenden, der mit dem Python-Subprozess Popen gestartet wurde?

Ich fand eine Diskussion über ein ähnliches Problem here, aber ich habe keine echten Antworten. Ich nehme an, dass ich entweder in der Lage sein muss, die Pipe zu schließen oder explizit den Sub-Subprozess (im Beispiel "Sleep") zu beenden, und das um die Pipe zu entsperren.

Ich habe auch versucht, auf SO die Antwort, sie zu finden, aber ich fand nur this und this und this, die nicht direkt so weit dieses Problem lösen, wie ich sagen kann (?).

Also die Sache, die ich tun möchte, ist in der Lage, einen Befehl in einem zweiten Thread auszuführen und alle seine Ausgabe zu erhalten, aber in der Lage, es sofort zu töten, wenn ich es wünsche. Ich könnte über eine Datei gehen und das oder ähnlich, aber ich denke, es sollte einen besseren Weg geben, dies zu tun?

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

Dies ist das Skript:

#!/bin/bash 
echo "sleeping five" 
sleep 5 
echo "slept five" 

Ausgabe

$ time python poc.py 
process: sleeping five 

real 0m5.053s 
user 0m0.044s 
sys 0m0.000s 
+0

Wenn 'process.kill()' genannt wird, './Ascript.sh' stirbt aber der' Schlaf 5' innen nicht. Interessant ist jedoch, dass Python nicht sofort zurückkehrt, nachdem './Ascript.sh' stirbt. –

Antwort

6

Ich denke, das Problem ist, dass process.kill() nur den unmittelbaren Kindprozess (Bash), nicht die Unterprozesse des Bash-Skriptes tötet.

Das Problem und die Lösung werden hier beschrieben:

Verwenden Popen (..., preexec_fn = os.setsid) eine Prozessgruppe und os zu erstellen. pgkill, um die gesamte Prozessgruppe zu beenden.

zB
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 
-1

Es sieht aus wie Sie ein Opfer von Python Supergrobkörnige Parallelität sein kann. Ändern Sie Ihr Skript dazu:

#!/bin/bash 
echo "sleeping five" 
sleep 5 
echo "sleeping five again" 
sleep 5 
echo "slept five" 

Und dann wird das Ausgangssignal:

process: sleeping five 

real 0m5.134s 
user 0m0.000s 
sys  0m0.010s 

Wenn das gesamte Skript ausgeführt, würde die Zeit 10s sein. Es sieht also so aus, als würde der Python-Kontroll-Thread erst laufen, wenn das Bash-Skript schläft. Und falls Sie Ihr Skript dies ändern:

#!/bin/bash 
echo "sleeping five" 
sleep 1 
sleep 1 
sleep 1 
sleep 1 
sleep 1 
echo "slept five" 

Dann ist der Ausgang wird:

process: sleeping five 

real 0m1.150s 
user 0m0.010s 
sys  0m0.020s 

Kurz gesagt, funktioniert der Code als logisch implementiert. :)

+0

Der Ruhezustand im Beispiel ist nur ein Platzhalter für jede längere Operation, die im Skript ausgeführt wird. Die Frage ist, wie bekomme ich die Pipe zum Herunterfahren, so dass der Code nicht auf Communcate() hängt? Alternativ, wie finde ich heraus, welche "Sub-Subprozesse" zu töten, um den Block freizugeben? – icecream

+0

Meinst du, es ist der Global Interpreter Lock schwer bei der Arbeit? http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock –

+0

Könnte sein? Irgendwelche Vorschläge? – icecream

0

Es scheint mir, dass der einfachste Weg, dies zu tun und umgehen die Multithreading-Probleme wäre, ein Kill-Flag aus dem Haupt-Thread, und überprüfen Sie es im Skript laufenden Thread kurz vor der Kommunikation, zu töten das Skript, wenn die Flagge True ist.

+0

Eigentlich ist es eher ein Problem mit dem Blockieren von Rohren als ein Multithreading-Problem. Ich brauche einen anderen Thread, damit ich ihn von irgendwoher herunterfahren kann. Wenn ich von der Shell "kill", tötet es das Skript und seine Unterprozesse. Pythons Kill kann das nicht, weil es so aussieht, als wolle die Pipe heruntergefahren werden. – icecream