Come posso chiudere lo stdout-pipe quando termina un processo avviato con il sottoprocesso di Python Popen?

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

Domanda

Mi chiedo se è possibile chiudere la pipa di comunicazione quando si uccide un sottoprocesso iniziato in un thread diverso. Se non chiamo communic (), allora kill () funzionerà come previsto, terminando il processo dopo un secondo anziché cinque.

Ho trovato una discussione su un problema simile qui , ma non ho avuto risposte reali. Presumo che devo essere in grado di chiudere la pipe o di uccidere esplicitamente il sottoprocesso (che è "sleep" nell'esempio) e ucciderlo per sbloccare la pipe.

Ho anche cercato di trovare la sua risposta su SO, ma ho trovato solo questo e this e this , che non affronta direttamente questo problema per quanto ne so ?).

Quindi la cosa che voglio fare è essere in grado di eseguire un comando in un secondo thread e ottenere tutto il suo output, ma essere in grado di ucciderlo istantaneamente quando lo desidero. Potrei passare attraverso un file e codificarlo o simile, ma penso che ci dovrebbe essere un modo migliore per farlo?

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

Questo è lo script:

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

Output

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s
È stato utile?

Soluzione

Penso che il problema sia che process.kill () uccide solo il processo figlio immediato (bash), non i sottoprocessi dello script bash.

Il problema e la soluzione sono descritti qui:

Usa Popen (..., preexec_fn = os.setsid) per creare un gruppo di processi e os.pgkill per uccidere l'intero gruppo di processi. ad esempio

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

Altri suggerimenti

Mi sembra che il modo più semplice per fare questo e eludere i problemi di multithreading sia impostare un kill flag dal thread principale e controllarlo nel thread in esecuzione dello script appena prima della comunicazione, uccidendo lo script quando la bandiera è True .

Sembra che tu possa essere vittima della concorrenza a grana super grossolana di Python. Cambia il tuo script in questo:

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

E quindi l'output diventa:

process: sleeping five

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

Se viene eseguito l'intero script, il tempo sarebbe 10 s. Quindi sembra che il thread di controllo python non venga effettivamente eseguito fino a quando lo script bash non viene dormito. Allo stesso modo, se cambi lo script in questo:

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

Quindi l'output diventa:

process: sleeping five

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

In breve, il codice funziona come implementato logicamente. :)

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top