Ни один из ответов здесь не отвечает всем моим потребностям.
- Нет потоков для стандартного вывода (нет очередей и т. Д.)
- Неблокирующая, так как мне нужно проверить, что происходит дальше
- Используйте PIPE так, как мне нужно, чтобы сделать несколько вещей, например, вывод потока, запись в файл журнала и возврат строковой копии вывода.
Немного предыстории: я использую ThreadPoolExecutor для управления пулом потоков, каждый из которых запускает подпроцесс и выполняет их параллелизм. (В Python2.7, но это должно работать и в более новых 3.x). Я не хочу использовать потоки только для сбора выходных данных, так как хочу, чтобы как можно больше было доступно для других целей (пул из 20 процессов использовал бы только 40 потоков для запуска; 1 для потока процесса и 1 для stdout ... и еще если хочешь стдерр наверное)
Я отбрасываю множество исключений и тому подобное здесь, так что это основано на коде, который работает в производстве. Надеюсь, я не испортил это в копии и вставке. Также, отзывы очень приветствуются!
import time
import fcntl
import subprocess
import time
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# Make stdout non-blocking when using read/readline
proc_stdout = proc.stdout
fl = fcntl.fcntl(proc_stdout, fcntl.F_GETFL)
fcntl.fcntl(proc_stdout, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def handle_stdout(proc_stream, my_buffer, echo_streams=True, log_file=None):
"""A little inline function to handle the stdout business. """
# fcntl makes readline non-blocking so it raises an IOError when empty
try:
for s in iter(proc_stream.readline, ''): # replace '' with b'' for Python 3
my_buffer.append(s)
if echo_streams:
sys.stdout.write(s)
if log_file:
log_file.write(s)
except IOError:
pass
# The main loop while subprocess is running
stdout_parts = []
while proc.poll() is None:
handle_stdout(proc_stdout, stdout_parts)
# ...Check for other things here...
# For example, check a multiprocessor.Value('b') to proc.kill()
time.sleep(0.01)
# Not sure if this is needed, but run it again just to be sure we got it all?
handle_stdout(proc_stdout, stdout_parts)
stdout_str = "".join(stdout_parts) # Just to demo
Я уверен, что здесь добавляются накладные расходы, но в моем случае это не проблема. Функционально он делает то, что мне нужно. Единственное, что я не решил, - почему это прекрасно работает для сообщений журнала, но я вижу, что некоторые print
сообщения появляются позже и все сразу.