питон: простой подход к убийству детей или сообщению об их успехе? - PullRequest
5 голосов
/ 03 августа 2010

Я хочу

  1. вызов команд оболочки (например, «сон» ниже) параллельно,
  2. отчет об их индивидуальных началах и завершениях и
  3. сможет убить их с помощью 'kill -9 parent_process_pid'.

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

Пока что мой подход (см. Код ниже) был:

  1. поместите subprocess.call (unix_command) в функцию-оболочку, которая сообщает о начале и завершении команды.
  2. вызов функции-оболочки с помощью multiprocess.Process.
  3. отслеживайте соответствующие пиды, сохраняйте их глобально и убивайте их в обработчике сигнала.

Я пытался избежать решения, которое периодически опрашивало процессы, но я не уверен, почему.

Есть ли лучший подход?

import subprocess,multiprocessing,signal
import sys,os,time

def sigterm_handler(signal, frame):
        print 'You killed me!'
        for p in pids:
                os.kill(p,9)
        sys.exit(0)

def sigint_handler(signal, frame):
        print 'You pressed Ctrl+C!'
        sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)

def f_wrapper(d):
        print str(d) + " start"
        p=subprocess.call(["sleep","100"])
        pids.append(p.pid)
        print str(d) + " done"

print "Starting to run things."

pids=[]

for i in range(5):
        p=multiprocessing.Process(target=f_wrapper,args=(i,))
        p.daemon=True
        p.start()

print "Got things running ..."

while pids:
        print "Still working ..."
        time.sleep(1)

Ответы [ 2 ]

4 голосов
/ 03 августа 2010

Как только subprocess.call возвращается, подпроцесс выполняется - и возвращаемое значение call является подпроцессом returncode. Таким образом, накапливая эти коды возврата в списке pids (что, между прочим, не синхронизируется между многопроцессорным добавлением и «основным» процессом) и отправляя им 9 сигналы «как если бы» они были идентификаторами процессов вместо возврата коды, безусловно, неправильно.

Еще одна вещь с вопросом, который определенно не так, это спецификация:

сможет убить их с помощью 'kill -9 parent_process_pid.

, поскольку -9 означает, что родительский процесс не может перехватить сигнал (это цель явного указания -9) - поэтому я думаю, что -9 здесь ложно.

Вы должны использовать threading вместо multiprocessing (каждый поток или процесс "няни", по сути, ничего не делает, кроме как ожидает своего подпроцесса, так зачем тратить впустую процессы на такую ​​легкую задачу? -); Вы также должны вызвать suprocess.Process в главном потоке (чтобы запустить подпроцесс и получить его .pid для включения в список) и передать полученный объект процесса потоку няни, который его ждет (и когда это сделано отчеты и удаляет его из списка). Список идентификаторов подпроцесса должен быть защищен блокировкой, так как основной поток и несколько потоков няни могут получить к нему доступ, и набор, вероятно, будет лучшим выбором, чем список (более быстрое удаление), так как вы не заботитесь ни о порядке, ни о порядке о том, как избежать дубликатов.

Итак, примерно (без тестирования, поэтому могут быть ошибки ;-) Я бы изменил ваш код на s / вещь вроде:

import subprocess, threading, signal
import sys, time

pobs = set()
pobslock = threading.Lock()
def numpobs():
    with pobslock:
        return len(pobs)

def sigterm_handler(signal, frame):
    print 'You killed me!'
    with pobslock:
        for p in pobs: p.kill()
    sys.exit(0)

def sigint_handler(signal, frame):
    print 'You pressed Ctrl+C!'
    sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)

def f_wrapper(d, p):
    print d, 'start', p.pid
    rc = p.wait()
    with pobslock:
        pobs.remove(p)
    print d, 'done, rc =', rc

print "Starting to run things."

for i in range(5):
    p = subprocess.Popen(['sleep', '100'])
    with pobslock:
        pobs.add(p)
    t = threading.Thread(target=f_wrapper, args=(i, p))
    t.daemon=True
    t.start()

print "Got things running ..."

while numpobs():
    print "Still working ..."
    time.sleep(1)
2 голосов
/ 03 августа 2010

Этот код (код ниже), кажется, работает для меня, убивая из "top" или ctrl-c из командной строки.Единственное реальное отличие от предложений Алекса состояло в том, чтобы заменить subprocess.Process на вызов subprocess.Popen (я не думаю, что subprocess.Process существует).

Код здесь также можно улучшить, заблокировав стандартный вывод, чтобы исключить возможность совпадения печати между процессами.

import subprocess, threading, signal
import sys, time

pobs = set()                            # set to hold the active-process objects
pobslock = threading.Lock()     # a Lock object to make sure only one at a time can modify pobs

def numpobs():
        with pobslock:
                return len(pobs)

# signal handlers
def sigterm_handler(signal, frame):
        print 'You killed me! I will take care of the children.'
        with pobslock:
                for p in pobs: p.kill()
        sys.exit(0)

def sigint_handler(signal, frame):
        print 'You pressed Ctrl+C! The children will be dealt with automatically.'
        sys.exit(0)

signal.signal(signal.SIGINT, sigint_handler)
signal.signal(signal.SIGTERM, sigterm_handler)


# a function to watch processes
def p_watch(d, p):
        print d, 'start', p.pid
        rc = p.wait()
        with pobslock:
                pobs.remove(p)
        print d, 'done, rc =', rc


# the main code
print "Starting to run things ..."
for i in range(5):
        p = subprocess.Popen(['sleep', '4'])
        with pobslock:
                pobs.add(p)
        # create and start a "daemon" to watch and report the process p.
        t = threading.Thread(target=p_watch, args=(i, p))
        t.daemon=True
        t.start()

print "Got things running ..."
while numpobs():
        print "Still working ..."
        time.sleep(1)
...