2009-10-26 12 views
9

Me pregunto si es posible apagar el conducto de comunicación al matar un subproceso iniciado en un hilo diferente. Si no llamo a communication(), kill() funcionará como se espera, terminando el proceso después de un segundo en lugar de cinco.¿Cómo cierro el stdout-pipe cuando se cancela un proceso iniciado con el subproceso python Popen?

Encontré una discusión de un problema similar here, pero no obtuve ninguna respuesta real. Supongo que o tengo que ser capaz de cerrar la tubería o matar explícitamente el subproceso secundario (que es "dormir" en el ejemplo) y matarlo para desbloquear la tubería.

también traté de encontrar la respuesta en su SO, pero sólo he encontrado this y this y this, que no aborda directamente este problema por lo que yo puedo decir (?).

Así que lo que quiero hacer es poder ejecutar un comando en un segundo hilo y obtener todos sus resultados, pero ser capaz de matarlo instantáneamente cuando lo desee. Podría ir a través de un archivo y la cola que o similar, pero creo que debería haber una mejor manera de hacer esto?

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

Este es el guión:

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

salida

$ time python poc.py 
process: sleeping five 

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

Cuando 'process.kill() se llama' './ascript.sh' muere pero el' 5' sueño dentro de él no lo hace. Es interesante, sin embargo, que Python no regrese inmediatamente después de que muera './Ascript.sh'. –

Respuesta

6

Creo que el problema es que process.kill() solo elimina el proceso secundario inmediato (bash), no los subprocesos del script bash.

El problema y la solución se describen a continuación:

Uso Popen (..., preexec_fn = os.setsid) para crear un grupo de procesos y el sistema operativo. pgkill para matar a todo el grupo de procesos.por ejemplo

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

Parece que puede ser víctima de súper gruesa concurrencia de grano de Python. Cambiar el script para esto:

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

Y entonces la salida se convierte en:

process: sleeping five 

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

Si todo el guión corrió, el tiempo sería 10s. Así que parece que el hilo de control de Python no se ejecuta hasta que el script bash duerme. Del mismo modo, si cambia de secuencia de comandos para esto:

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

A continuación, la salida se convierte en:

process: sleeping five 

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

En resumen, el código funciona implementado como lógicamente. :)

+0

La suspensión en el ejemplo es solo un marcador de posición para cualquier operación prolongada que se realice en el script. La pregunta es, ¿cómo puedo cerrar la tubería para que el código no se cuelgue en communcate()? Alternativamente, ¿cómo puedo averiguar qué "subprocesos" matar para liberar el bloque? – icecream

+0

¿Quiere decir que es la Intérprete Global Lock trabajando duro? http://docs.python.org/c-api/init.html#thread-state-and-the-global-interpreter-lock –

+0

¿Podría ser? ¿Alguna sugerencia? – icecream

0

Me parece que la manera más fácil de hacer esto y eludir los problemas de subprocesos múltiples sería establecer un indicador de muerte desde el hilo principal y verificarlo en el hilo que ejecuta el guión justo antes de la comunicación, matando el script cuando la bandera es True.

+0

En realidad, es más un problema con el bloqueo de tuberías que un problema de subprocesamiento múltiple. Necesito un hilo diferente para poder cerrarlo desde algún lado. Si hago "kill" desde el shell, mata el script y sus subprocesos. La muerte de Python no puede hacer esto porque parece querer que la tubería se cierre. – icecream

Cuestiones relacionadas