2009-10-26 13 views
9

Zastanawiam się, czy możliwe jest wyłączenie rury komunikacyjnej podczas zabijania podprocesu rozpoczętego w innym wątku. Jeśli nie wywołasz komendy(), to funkcja kill() będzie działać zgodnie z oczekiwaniami, kończąc proces po jednej sekundzie zamiast pięciu.Jak zamknąć stdout-pipe przy zabijaniu procesu rozpoczętego w podprocesie Pythona?

Znalazłem omówienie podobnego problemu here, ale nie dostałem prawdziwych odpowiedzi. Zakładam, że albo będę musiał zamknąć rurę, albo wyraźnie zabić podproces (który jest "uśpiony" w przykładzie) i zabić, aby odblokować rurę.

Próbowałem również znaleźć odpowiedź na nią, ale znalazłem tylko this i this i this, które nie rozwiązują bezpośrednio tego problemu, o ile mogę powiedzieć (?).

Więc chcę zrobić, aby móc uruchomić polecenie w drugim wątku i uzyskać wszystkie jego wyniki, ale być w stanie zabić go natychmiast, kiedy tak chcę. Mogę przejść przez plik i ogon tego lub podobnego, ale myślę, że powinien być lepszy sposób to zrobić?

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

Jest to scenariusz:

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

Wyjście

$ time python poc.py 
process: sleeping five 

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

Kiedy 'process.kill()' nazywa, './ascript.sh' umiera ale' sen 5' wewnątrz niej nie ma. Interesujące jest jednak to, że Python nie wraca natychmiast po śmierci './Ascript.sh'. –

Odpowiedz

6

Myślę, że problem polega na tym, że process.kill() zabija tylko bezpośredni proces potomny (bash), a nie podprocesy skryptu bash.

Problem i rozwiązanie opisane tu:

Zastosowanie Popen (..., preexec_fn = os.setsid), aby utworzyć grupę procesu i systemu operacyjnego. pgkill, aby zabić całą grupę procesów.np

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

Wygląda na to może być ofiarą Pythona bardzo gruboziarnistej współbieżności. Zmień swój skrypt do tego:

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

A potem wyjście uruchamia się:

process: sleeping five 

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

Jeżeli Cały skrypt, czas byłby 10s. Wygląda na to, że wątek kontrolny python nie działa, dopóki skrypt basha nie zostanie uśpiony. Podobnie, jeśli zmienisz skrypt do tego:

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

Następnie wyjście uruchamia się:

process: sleeping five 

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

W skrócie, Twój kod działa jako logicznie wdrażane. :)

+0

Sen w tym przykładzie jest tylko symbolem zastępczym dla długotrwałej operacji wykonywanej w skrypcie. Pytanie brzmi: w jaki sposób mogę wyłączyć potok, aby kod nie zawieszał się w funkcji communcate()? Alternatywnie, w jaki sposób mogę dowiedzieć się, które "pod-podprocesy" zabić, aby zwolnić blok? – icecream

+0

Czy masz na myśli to, że globalna blokada tłumacza jest ciężka w pracy? http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock –

+0

Może być? Jakieś sugestie? – icecream

0

Wydaje mi się, że najłatwiejszym sposobem na zrobienie tego i uniknięcie problemów z wielowątkowością byłoby ustawienie flagi zabicia z głównego wątku i sprawdzenie jej w wątku uruchomionym w skrypcie tuż przed komunikacją, zabijanie skrypt, gdy flaga jest True.

+0

W rzeczywistości jest to większy problem z blokowaniem rur niż problem z wielowątkowością. Potrzebuję innej nici, żeby móc ją gdzieś wyłączyć. Jeśli zrobię "kill" z powłoki, zabije on skrypt i jego podprocesy. Python's kill nie robi tego, ponieważ wydaje się, że chce zamknąć potok. – icecream

Powiązane problemy