Как закрыть канал stdout-pipe при завершении процесса, запущенного с помощью подпроцесса Python Popen?

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

Вопрос

Интересно, можно ли отключить канал связи при завершении подпроцесса, запущенного в другом потоке?Если я не вызываю метод communication(), то kill() будет работать как положено, завершая процесс через одну секунду вместо пяти.

нашел обсуждение похожей проблемы здесь, но реальных ответов я не получил.Я предполагаю, что мне нужно либо закрыть канал, либо явно завершить подпроцесс (в данном примере это «сон») и убить его, чтобы разблокировать канал.

Я тоже пытался найти ответ ей на ТАК, но нашел только этот и этот и этот, которые, насколько я могу судить (?), не решают эту проблему напрямую.

Итак, я хочу иметь возможность запускать команду во втором потоке и получать весь ее вывод, но иметь возможность мгновенно уничтожить ее, когда я того пожелаю.Я мог бы пройти через файл и отслеживать его или что-то подобное, но я думаю, что должен быть лучший способ сделать это?

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

Это сценарий:

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

Выход

$ time python poc.py 
process: sleeping five

real    0m5.053s
user    0m0.044s
sys 0m0.000s
Это было полезно?

Решение

Я думаю, проблема в том, что процесс.kill() убивает только непосредственный дочерний процесс (bash), а не подпроцессы сценария bash.

Проблема и решение описаны здесь:

Используйте Popen(..., preexec_fn=os.setsid) для создания группы процессов и os.pgkill для уничтожения всей группы процессов.например

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

Другие советы

Мне кажется, что самый простой способ сделать это и обойти проблемы многопоточности - это установить флаг уничтожения из основного потока и проверить его в потоке выполнения сценария непосредственно перед связью, убивая сценарий, когда флаг установлен. True.

Похоже, вы стали жертвой сверхгрубого параллелизма в Python.Измените свой скрипт на это:

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

И тогда вывод становится:

process: sleeping five

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

Если бы весь сценарий выполнялся, время было бы 10 секунд.Итак, похоже, что поток управления Python на самом деле не запускается до тех пор, пока скрипт bash не перейдет в режим сна.Аналогично, если вы измените свой сценарий на этот:

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

Тогда вывод становится:

process: sleeping five

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

Короче говоря, ваш код работает как логически реализованный.:)

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top