Параллельный подпроцесс Python - PullRequest
18 голосов
/ 17 марта 2012

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

Ответы [ 3 ]

18 голосов
/ 17 марта 2012

Вы можете сделать это в одном потоке.

Предположим, у вас есть скрипт, который печатает строки в случайное время:

#!/usr/bin/env python
#file: child.py
import os
import random
import sys
import time

for i in range(10):
    print("%2d %s %s" % (int(sys.argv[1]), os.getpid(), i))
    sys.stdout.flush()
    time.sleep(random.random())

И вы хотите собрать выходные данные, как толькокогда он станет доступен, вы можете использовать select в системах POSIX, как @ zigg предлагает :

#!/usr/bin/env python
from __future__ import print_function
from select     import select
from subprocess import Popen, PIPE

# start several subprocesses
processes = [Popen(['./child.py', str(i)], stdout=PIPE,
                   bufsize=1, close_fds=True,
                   universal_newlines=True)
             for i in range(5)]

# read output
timeout = 0.1 # seconds
while processes:
    # remove finished processes from the list (O(N**2))
    for p in processes[:]:
        if p.poll() is not None: # process ended
            print(p.stdout.read(), end='') # read the rest
            p.stdout.close()
            processes.remove(p)

    # wait until there is something to read
    rlist = select([p.stdout for p in processes], [],[], timeout)[0]

    # read a line from each process that has output ready
    for f in rlist:
        print(f.readline(), end='') #NOTE: it can block

Более портативное решение (которое должно работать в Windows, Linux, OSX) может использовать потоки считывателя для каждого процесса, см. Неблокирующее чтение для подпроцесса. PIPE в python .

Здесь * os.pipe() на основерешение, которое работает в Unix и Windows:

#!/usr/bin/env python
from __future__ import print_function
import io
import os
import sys
from subprocess import Popen

ON_POSIX = 'posix' in sys.builtin_module_names

# create a pipe to get data
input_fd, output_fd = os.pipe()

# start several subprocesses
processes = [Popen([sys.executable, 'child.py', str(i)], stdout=output_fd,
                   close_fds=ON_POSIX) # close input_fd in children
             for i in range(5)]
os.close(output_fd) # close unused end of the pipe

# read output line by line as soon as it is available
with io.open(input_fd, 'r', buffering=1) as file:
    for line in file:
        print(line, end='')
#
for p in processes:
    p.wait()
6 голосов
/ 17 марта 2012

Вы также можете собирать стандартный вывод из нескольких подпроцессов одновременно, используя twisted:

#!/usr/bin/env python
import sys
from twisted.internet import protocol, reactor

class ProcessProtocol(protocol.ProcessProtocol):
    def outReceived(self, data):
        print data, # received chunk of stdout from child

    def processEnded(self, status):
        global nprocesses
        nprocesses -= 1
        if nprocesses == 0: # all processes ended
            reactor.stop()

# start subprocesses
nprocesses = 5
for _ in xrange(nprocesses):
    reactor.spawnProcess(ProcessProtocol(), sys.executable,
                         args=[sys.executable, 'child.py'],
                         usePTY=True) # can change how child buffers stdout
reactor.run()

См. Использование процессов в витой .

4 голосов
/ 17 марта 2012

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

Вы делаете должны быть осторожны, чтобы случайно не заблокировать их, хотя, если вы не собираетесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...