Comment puis-je fermer le stdout-pipe lorsque vous tuez un processus démarré avec le sous-processus python Popen?

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

Question

Je me demande s'il est possible de fermer le canal de communication lors de la suppression d'un sous-processus démarré dans un autre thread. Si je n'appelle pas communic (), alors kill () fonctionnera comme prévu, mettant fin au processus après une seconde au lieu de cinq.

J'ai trouvé une discussion sur un problème similaire ici , mais je n'ai pas eu de véritable réponse. Je suppose que je dois être capable de fermer le canal ou de supprimer explicitement le sous-sous-processus (ce qui est "sommeil" dans l'exemple) et de le supprimer pour débloquer le canal.

J'ai aussi essayé de trouver la réponse sur SO, mais je n'ai trouvé que ceci et ceci et this , qui ne résolvent pas directement ce problème pour autant que je sache ( ?).

Donc, ce que je veux faire, c'est pouvoir exécuter une commande dans un deuxième thread et obtenir tout son résultat, mais pouvoir le tuer instantanément lorsque je le souhaite. Je pourrais utiliser un fichier et une queue similaire ou similaire, mais je pense qu’il devrait exister un meilleur moyen de le faire?

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

Voici le script:

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

Sortie

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s
Était-ce utile?

La solution

Je pense que le problème est que process.kill () ne tue que le processus enfant immédiat (bash), pas les sous-processus du script bash.

Le problème et la solution sont décrits ci-dessous:

Utilisez Popen (..., preexec_fn = os.setsid) pour créer un groupe de processus et os.pgkill pour supprimer l’ensemble du groupe de processus. par exemple

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

Autres conseils

Il me semble que le moyen le plus simple de le faire et de contourner les problèmes de multithreading serait de définir un indicateur d'interruption du thread principal et de le rechercher dans le thread d'exécution du script juste avant la communication, afin de tuer le script lorsque l'indicateur est True .

Il semble que vous soyez victime de la concurrence concurrente à grain grossier de Python. Changez votre script en ceci:

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

Et alors la sortie devient:

process: sleeping five

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

Si le script entier était exécuté, le temps serait de 10 secondes. Il semble donc que le thread de contrôle python ne s'exécute pas tant que le script bash n'a pas dormi. De même, si vous changez votre script en ceci:

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

La sortie devient alors:

process: sleeping five

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

En bref, votre code fonctionne comme implémenté logiquement. :)

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top