Использование select.poll
: необходимо передать объекты методом fileno
или дескрипторами реального файла (целыми числами) :
import os, sys, select, subprocess
args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)
while True:
rlist, wlist, xlist = select.select([p1.stdout, p2.stdout], [], [])
for stdout in rlist:
sys.stdout.write(os.read(stdout.fileno(), 1024))
Вы будете видеть, что он делает паузу каждые две секунды, а затем производит больше выходных данных по мере их появления. «Хитрость» заключается в том, что p1.stdout
- это обычный файловый объект с методом fileno
, который возвращает номер дескриптора файла. Это все, что нужно select
.
Обратите внимание, что я читаю из stdout
, используя os.read
вместо простого вызова stdout.read
. Это связано с тем, что такой вызов, как stdout.read(1024)
, заставит вашу программу ждать, пока не будет прочитано запрошенное количество байтов. Меньшее количество байтов возвращается только при достижении EOF, но, поскольку EOF никогда не достигается, вызов stdout.read
блокируется до тех пор, пока не будет прочитано не менее 1024 байтов.
Это не похоже на функцию os.read
, которая не имеет никаких сомнений в отношении досрочного возврата, когда доступно меньшее количество байтов - она сразу же возвращает то, что доступно. Другими словами, получение менее 1024 байтов от os.read(stdout.fileno(), 1024)
не является признаком того, что stdout
был закрыт.
Использование select.epoll
практически идентично, за исключением того, что вы получаете «сырой» файловый дескриптор (FD) обратно, который вам необходим os.read
, чтобы иметь возможность читать:
import os, sys, select, subprocess
args = ['sh', '-c', 'while true; do date; sleep 2; done']
p1 = subprocess.Popen(args, stdout=subprocess.PIPE)
p2 = subprocess.Popen(args, stdout=subprocess.PIPE)
poll = select.poll()
poll.register(p1.stdout)
poll.register(p2.stdout)
while True:
rlist = poll.poll()
for fd, event in rlist:
sys.stdout.write(os.read(fd, 1024))
Закрытый FD сигнализируется возвращаемым событием select.POLLHUP
. Затем вы можете вызвать метод unregister
и, наконец, выйти из цикла, когда все FD закрыты.
Наконец, позвольте мне заметить, что вы, конечно, могли бы создать словарь с отображением файловых дескрипторов обратно на файловые объекты и, следовательно, на запущенные вами процессы.