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()
Он будет фактически останавливаться и ждать на этой линии, пока не получит больше ввода.
Надеюсь, это поможет кому-то!