Python дождитесь, пока данные будут в sys.stdin

моя проблема заключается в следующем:

Мои питоны script получают данные через sys.stdin, но ему нужно дождаться появления новых данных на sys.stdin.

Как описано в manpage из python, я использую следующий код, но он полностью перегружает мой процессор.

#!/usr/bin/python -u
import sys
while 1:
     for line in sys.stdin.readlines():
         do something useful

Есть ли какой-либо хороший способ решить проблему с высоким значением процессора?

Изменить:

Все ваши решения не работают. Я даю вам точно мою проблему.

Вы можете настроить демон apache2, который он отправляет каждому лондону в программу, а не записывать в файл журнала.

Это выглядит примерно так:

CustomLog "|/usr/bin/python -u /usr/local/bin/client.py" combined

Apache2 ожидает от моего script, что он работает всегда, ждет данных на sys.stdin и анализирует его, тогда есть данные.

Если я использую только цикл for, то script выйдет, потому что в точке нет данных в sys.stdin, а apache2 скажет, что ваш script неожиданно вышел.

Если я использую цикл while, мой script будет использовать 100% -ное использование процессора.

Ответ 1

Следующее должно работать.

import sys
for line in sys.stdin:
    # whatever

Обоснование:

Код будет перебирать строки в stdin по мере их поступления. Если поток все еще открыт, но нет полной строки, цикл будет зависать до тех пор, пока не встретится символ новой строки (и вся строка вернется) или поток закрыт (и возвращается все, что осталось в буфере).

Как только поток был закрыт, больше данных не может быть записано или записано из stdin. Период.

Причина, по которой ваш код перегружал ваш процессор, заключается в том, что после закрытия stdin любые последующие попытки итерации по stdin немедленно возвращаются без каких-либо действий. По существу ваш код был эквивалентен следующему.

for line in sys.stdin:
    # do something

while 1:
    pass # infinite loop, very CPU intensive

Возможно, было бы полезно, если бы вы опубликовали, как вы записывали данные в stdin.

EDIT:

Python будет (для целей циклов, итераторов и readlines() считать поток закрыт, когда он встречает символ EOF. Вы можете попросить python прочитать больше данных после этого, но вы не можете использовать какой-либо из предыдущих методов. Страница руководства python рекомендует использовать

import sys
while True:
    line = sys.stdin.readline()
    # do something with line

Когда встречается символ EOF, readline возвращает пустую строку. Следующий вызов readline будет работать нормально, если поток все еще открыт. Вы можете проверить это самостоятельно, выполнив команду в терминале. Нажатие ctrl + D заставит терминал записать символ EOF в stdin. Это приведет к завершению работы первой программы в этом сообщении, но последняя программа продолжит считывать данные до тех пор, пока поток фактически не будет закрыт. Последняя программа не должна составлять 100% вашего процессора, так как readline будет ждать, пока не будут возвращены данные, а не будет возвращена пустая строка.

У меня проблема с циклом занятости, когда я пытаюсь использовать readline из фактического файла. Но при чтении из stdin, readline счастливо блокирует.

Ответ 2

Ну, я буду придерживаться этих строк кода.

#!/usr/bin/python
import sys
import time
while 1:
    time.sleep(0.01)
    for line in sys.stdin:
        pass # do something useful

Если я не использую time.sleep, script создаст слишком высокую нагрузку на использование процессора.

Если я использую:

for line in sys.stdin.readline():

Он будет анализировать только одну строку за 0,01 секунды, а производительность apache2 действительно плоха Большое спасибо за ваши ответы.

С наилучшими пожеланиями Abalus

Ответ 3

Используйте это:

#!/usr/bin/python
import sys
for line in sys.stdin.readlines():
    pass # do something useful

Ответ 4

Я знаю, что я приношу старые вещи, но это кажется одним из лучших хитов по этой теме. Решение, принятое в Abalus, имеет фиксированное время. Спящий каждый цикл, считает, что stdin фактически пуст, и программа должна работать на холостом ходу или есть много строк, ожидающих обработки. Небольшая модификация позволяет программе быстро обрабатывать все сообщения и ждать, только если очередь фактически пуста. Таким образом, только одна строка, которая приходит во время периода ожидания, может ждать, остальные обрабатываются без какого-либо отставания.

Этот пример просто реверсирует входные строки, если вы отправляете только одну строку, на которую отвечает второй (или любой другой период времени ожидания), но также может очень быстро обрабатывать что-то вроде "ls -l | reverse.py", Загрузка процессора для такого подхода минимальна даже для встроенных систем, таких как OpenWRT.

import sys
import time

while True:
  line=sys.stdin.readline().rstrip()
  if line:       
    sys.stdout.write(line[::-1]+'\n')
  else:
    sys.stdout.flush()
    time.sleep(1)

Ответ 5

Я возвращаюсь к проблеме через долгое время. Проблема заключается в том, что Apache рассматривает CustomLog как файл - что-то, что он может открыть, написать, закрыть, а затем снова открыть позже. Это заставляет процесс получения сказать, что входной поток закрыт. Однако это не означает, что поток входных процессов не может быть записан снова, так как любой процесс, который был записан во входной поток, больше не будет записываться в него.

Лучший способ справиться с этим - установить обработчик и позволить ОС знать, чтобы вызвать обработчик всякий раз, когда ввод записывается на стандартный ввод. Обычно вам следует избегать сильной зависимости от обработки сигналов сигнала ОС, поскольку они относительно дороги. Тем не менее, копирование мегабайта текста в последующие только создало два события SIGIO, поэтому в этом случае все в порядке.

fancyecho.py

import sys
import os
import signal
import fcntl
import threading

io_event = threading.Event()

# Event handlers should generally be as compact as possible.
# Here all we do is notify the main thread that input has been received.
def handle_io(signal, frame):
    io_event.set()

# invoke handle_io on a SIGIO event
signal.signal(signal.SIGIO, handle_io)
# send io events on stdin (fd 0) to our process 
assert fcntl.fcntl(0, fcntl.F_SETOWN, os.getpid()) == 0
# tell the os to produce SIGIO events when data is written to stdin
assert fcntl.fcntl(0, fcntl.F_SETFL, os.O_ASYNC) == 0

print("pid is:", os.getpid())
while True:
    data = sys.stdin.read()
    io_event.clear()
    print("got:", repr(data))
    io_event.wait()

Как вы можете использовать эту игрушечную программу. Выход был очищен из-за чередования ввода и вывода.

$ echo test | python3 fancyecho.py &
[1] 25487
pid is: 25487
got: 'test\n'
$ echo data > /proc/25487/fd/0
got: 'data\n'
$

Ответ 6

Я знаю, что это старый поток, но я наткнулся на ту же проблему и понял, что это больше связано с тем, как был вызван script, а не с проблемой script. По крайней мере, в моем случае это оказалось проблемой с "системной оболочкой" на debian (то есть: с чем связано /bin/sh - это то, что apache использует для выполнения команды, которую обрабатывают команды CustomLog). Подробнее здесь: http://www.spinics.net/lists/dash/msg00675.html

НТН, - steve

Ответ 7

Это работает для меня, код /tmp/alog.py:

#! /usr/bin/python

import sys

fout = open("/tmp/alog.log", "a")

while True:
    dat = sys.stdin.readline()
    fout.write(dat)
    fout.flush()

в http.conf:

CustomLog "|/tmp/alog.py" combined

Ключ не используется

for dat in sys.stdin:

Подождите, ничего не получится. И для тестирования помните fout.flush(), иначе вы можете не видеть вывод. Я тестирую Fedora 15, python 2.7.1, Apache 2.2, а не загрузку cpu, alog.py будет существовать в памяти, если вы можете увидеть ее.

Ответ 8

У меня была аналогичная проблема, когда python ждет отправителя (пользователя или другую программу), чтобы закрыть поток до начала цикла. Я решил это, но это было явно не pythonic, поскольку мне приходилось прибегать к while True: и sys.stdin.readline()

В конце концов я нашел ссылку в комментарии в другом сообщении в модуль под названием io, который является альтернативой стандартным файловым объектам. В Python 3 это значение по умолчанию. Из того, что я могу понять, Python 2 рассматривает stdin как обычный файл, а не поток.

Попробуй, это сработало для меня:

sys.stdin = io.open(sys.stdin.fileno())  # default is line buffering, good for user input

for line in sys.stdin:
    # Do stuff with line

Ответ 9

Это фактически работает безупречно (т.е. нет процессорной обработки) - когда вы вызываете script из оболочки, например:

tail -f input-file | yourscript.py

Очевидно, что это не идеально - так как тогда вам нужно записать все соответствующие stdout в этот файл -

но он работает без больших накладных расходов! А именно из-за использования readline() - я думаю:

while 1:
        line = sys.stdin.readline()

Он будет фактически останавливаться и ждать на этой линии, пока не получит больше ввода.

Надеюсь, это поможет кому-то!