Piped Python скрипт занимает 100% процессора при чтении из сломанной трубы


У меня есть два скрипта Python, запущенных на машине Ubuntu Linux. Первый отправляет все свои выходные данные в stdout, второй читает из stdin. Они соединены простой трубой, то есть примерно так:

./step1.py <some_args> | ./step2.py <some_other_args>

То, что делает step2, заключается в том, что он читает входные строки в бесконечном цикле и обрабатывает их:

while True:
    try:
        l = sys.stdin.readline()
        # processing here

Step1 время от времени выходит из строя. Когда это происходит (не уверен, что всегда, но, по крайней мере, в нескольких случаях) , то вместо сбоя/остановки Шаг 2 сходит с ума и начинает забирать 100% процессора, пока я вручную не убью его.

Почему это происходит и как я могу сделать шаг 2 более надежным, чтобы он остановился, когда труба сломана?

Спасибо!

2 3

2 ответа:

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

Во втором (читающем) сценарии вы можете использовать идиому:

for line in sys.stdin:
    process(line)
Таким образом, вы не окажетесь в бесконечном цикле. Кроме того, вы на самом деле не показали, какое исключение вы пытаетесь поймать во втором скрипте, но я предполагаю, что время от времени вы будете испытывать ошибку "сломанной трубы", которую вы можете и должны поймать, как описано здесь: Как обрабатывать сломанную трубу (SIGPIPE) в в Python?

Тогда вся схема могла бы выглядеть так:

try:
    for line in sys.stdin:
        process(line)
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error

Когда step1 умирает, у вас есть цикл while с попыткой оператора, который вызовет исключение. Таким образом, вы будете постоянно пытаться и терпеть неудачу, используя 100% процессора, поскольку readline не будет блокировать, когда он создает исключение.

Либо добавьте временную задержку к чтению с помощью time.sleep, либо, еще лучше, обратите внимание на ошибки, которые выбрасывает readline, и поймайте конкретную ошибку, которая выбрасывается, когда step1 останавливается и выходит из программы вместо того, чтобы пытаться читать из мертвого канала.

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