Ни один из ответов здесь не отвечает всем моим потребностям.
- Нет потоков для стандартного вывода (нет очередей и т. Д.)
- Неблокирование, так как мне нужно проверить, что происходит
- Используйте ТРУБУ, как мне нужно, чтобы сделать несколько вещей, например, выводить поток, записывать в файл журнала и возвращать строковую копию вывода.
Немного предыстории: я использую ThreadPoolExecutor для управления пулом потоков, каждый из которых запускает подпроцесс и выполняет их параллелизм. (В Python2.7, но это должно работать и в более новых 3.x). Я не хочу использовать потоки только для сбора выходных данных, так как хочу, чтобы как можно больше было доступно для других целей (пул из 20 процессов использовал бы только 40 потоков для запуска; 1 для потока процесса и 1 для stdout ... и больше, если вы хотите, stderr, я думаю)
Я отбрасываю множество исключений и тому подобное, так что это на основе кода, который работает в производстве. Надеюсь, я не испортил это в копии и вставке. Также, отзывы очень приветствуются!
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
сообщения появляются позже и все сразу.