2012-03-16 47 views
10

Tôi muốn chạy nhiều quy trình song song với khả năng lấy thiết bị xuất chuẩn bất kỳ lúc nào. Tôi nên làm như thế nào? Tôi có cần chạy chuỗi cho mỗi cuộc gọi subprocess.Popen() không?Quy trình con của Python song song

+0

bản sao có thể có của [cách chạy một số tệp thực thi bằng python?] (Http://stackoverflow.com/questions/9724499/how-to-run-several-executable-using-python) –

+0

liên quan: Đây là cách để [ chạy nhiều lệnh shell (và tùy chọn bắt đầu ra) đồng thời] (http://stackoverflow.com/a/23616229/4279) – jfs

Trả lời

13

Bạn có thể làm điều đó trong một chuỗi.

Giả sử bạn có một kịch bản mà in dòng vào những thời điểm ngẫu nhiên:

#!/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()) 

Và bạn muốn thu thập các kết quả ngay sau khi nó trở nên có sẵn, bạn có thể sử dụng trên các hệ thống select POSIX như @zigg suggested:

#!/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 

Một giải pháp di động khác (có thể hoạt động trên Windows, Linux, OSX) có thể sử dụng chuỗi trình đọc cho mỗi quá trình, xem Non-blocking read on a subprocess.PIPE in python.

Dưới đây là os.pipe() giải pháp dựa trên hoạt động trên Unix và 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() 
+2

Bạn dường như ghép mọi stdout của trẻ em thành một fd duy nhất (output_fd) trong giải pháp cuối cùng của bạn. Điều gì xảy ra nếu 2 trẻ em in cùng một lúc, điều đó sẽ không làm hỏng đầu ra (ví dụ: 'AAA \ n' + 'BBB \ n' -> 'ABBB \ nAA \ n') – dan3

+1

@ dan3: Đó là một mối quan tâm hợp lệ . 'write' nhỏ hơn' PIPE_BUF' byte là nguyên tử. Nếu không, dữ liệu từ nhiều quy trình có thể được xen kẽ. POSIX yêu cầu ít nhất 512 byte. Trên Linux, 'PIPE_BUF' là 4096 byte. – jfs

+0

Dưới đây là một câu hỏi tương tự mà tôi đã đăng gần đây tại đây, http://stackoverflow.com/questions/36624056/running-a-secondary-script-in-a-new-terminal sẽ thật tuyệt vời nếu bạn có thể giúp đỡ, cảm ơn mọi trường hợp . –

4

Bạn không cần chạy chuỗi cho từng quy trình. Bạn có thể xem qua các luồng stdout cho từng quy trình mà không chặn chúng và chỉ đọc từ chúng nếu chúng có sẵn dữ liệu để đọc.

Bạn do phải cẩn thận để không vô tình chặn chúng, tuy nhiên, nếu bạn không có ý định.

+0

Tôi làm 'p = subprocess.Popen (…)' và sau đó 'print p.communicate() [0] 'nhiều lần. Nhưng 'communication()' chỉ đợi trước khi quá trình kết thúc. – sashab

+1

Có, đó là lý do tại sao bạn không thể sử dụng 'communication()' nếu bạn muốn sử dụng một chuỗi đơn. Có nhiều cách khác để lấy stdout ngoài 'communication()'. – Amber

+2

Có thể bạn cần xem xét mô-đun [select] (http://docs.python.org/library/select.html) để chờ nhiều quy trình phụ cùng một lúc. – zigg

6

Bạn cũng có thể thu thập từ nhiều stdout subprocesses đồng thời sử dụng 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() 

Xem Using Processes in Twisted.