Python等到数据在sys.stdin中

时间:2011-08-14 10:37:25

标签: python wait

我的问题如下:

我的pythons脚本通过sys.stdin接收数据,但需要等到sys.stdin上有新数据可用。

如python的联机帮助页中所述,我使用以下代码,但它完全超载了我的cpu。

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

有没有什么好方法可以解决高CPU使用率?

编辑:

您的所有解决方案均无效。 我告诉你我的问题。

您可以配置将每个日志发送到程序而不是写入日志文件的apache2守护程序。

看起来像这样:

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

Apache2希望我的脚本始终运行,等待sys.stdin上的数据并解析它然后有数据。

如果我只使用for循环,脚本将退出,因为在某一点上,sys.stdin中没有数据,而apache2会说哦,你的脚本意外退出。

如果我使用while true循环,我的脚本将使用100%cpu使用。

9 个答案:

答案 0 :(得分:20)

以下内容应该有效。

import sys
for line in sys.stdin:
    # whatever

理由:

代码将在stdin中进行迭代,因为它们进来。如果流仍然打开,但没有完整的行,那么循环将挂起,直到遇到换行符(并返回整行)或者关闭流(并返回缓冲区中剩余的内容)。

关闭流后,不能再向stdin写入数据或从stdin读取数据。周期。

你的代码重载你的cpu的原因是,一旦stdin被关闭,任何后续迭代stdin的尝试都将立即返回而不做任何事情。实质上,您的代码等同于以下内容。

for line in sys.stdin:
    # do something

while 1:
    pass # infinite loop, very CPU intensive

如果你发布了如何将数据写入stdin,那么它可能会有用。

修改

Python将(for for循环,迭代器和readlines()的目的考虑在遇到EOF字符时关闭一个流。你可以让python在此之后读取更多数据,但你不能使用任何以前的方法。 python手册页建议使用

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

当遇到EOF字符时,readline将返回一个空字符串。如果流仍处于打开状态,则对readline的下一次调用将正常运行。您可以通过在终端中运行命令来自行测试。按ctrl + D将使终端将EOF字符写入stdin。这将导致此帖子中的第一个程序终止,但最后一个程序将继续读取数据,直到实际关闭流。最后一个程序不应该100%你的CPU作为readline会等到有数据要返回而不是返回一个空字符串。

当我尝试从实际文件中读取readline时,我只遇到繁忙循环的问题。但是当从stdin读取时,readline很快就会阻塞。

答案 1 :(得分:2)

使用此:

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

答案 2 :(得分: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,脚本会对cpu使用产生过高的负载。

如果我使用:

for line in sys.stdin.readline():

它只会在0.01秒内解析一行,并且apache2的性能非常糟糕 非常感谢你的回答。

最好的问候 Abalus

答案 3 :(得分:2)

这实际上是完美无缺的(即没有跑道CPU) - 当您从shell调用脚本时,如下所示:

$date = new DateTime('0000-00-00 00:00'); if (DateTime::createFromFormat('Y-m-d', $date->format('Y-m-d'))) { // Valid } else { // Not valid }

显然,这并不理想 - 因为你必须将所有相关的标准输出写入该文件 -

但它的工作没有太大的开销! 即因为使用tail -f input-file | yourscript.py - 我认为:

readline()

它实际上会在该行停止并等待,直到它获得更多输入。

希望这有助于某人!

答案 4 :(得分:1)

我知道我将旧的东西带入生活,但这似乎是该主题的热门话题之一。 Abalus已经确定的解决方案是固定time.sleep每个循环,如果stdin实际上是空的并且程序应该是空闲或者有很多行等待处理。一个小的修改使程序快速处理所有消息,并且仅在队列实际为空时才等待。因此,只有一条在睡眠期间到达的线路可以等待,其他线路的处理没有任何延迟。

这个例子只是反转输入行,如果你只提交一行它会在一秒内响应(或者你的睡眠时间设置不同),但也可以快速处理类似“ls -l | reverse.py”的内容。即使在像OpenWRT这样的嵌入式系统上,这种方法的CPU负载也很小。

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 :(得分:1)

我遇到过类似的问题,其中python在循环开始执行之前等待发送者(无论是用户还是其他程序)关闭流。我已经解决了它,但它显然不是pythonic,因为我不得不诉诸while True:sys.stdin.readline()

我最终在另一个post的评论中找到了一个名为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

答案 6 :(得分:1)

经过很长一段时间我才回到问题面前。问题似乎是Apache将CustomLog视为一个文件 - 它可以打开,写入,关闭,然后在以后重新打开。这会导致接收过程被告知其输入流已关闭。但是,这并不意味着进程输入流不能再次写入,只是无论哪个进程写入输入流都不会再次写入。

处理这个问题的最好方法是设置一个处理程序,让操作系统知道在输入写入标准输入时调用处理程序。通常,您应该避免严重依赖OS信号事件处理,因为它们相对昂贵。但是,将一兆字节的文本复制到后面只会产生两个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'
$

答案 7 :(得分:0)

我知道这是一个旧线程,但我偶然发现了同样的问题并且发现这更多地与调用脚本的方式有关,而不是脚本的问题。至少在我的情况下,这被证明是debian上的'system shell'的问题(即:what / bin / sh链接到 - 这是apache用来执行CustomLog管道的命令)。更多信息:http://www.spinics.net/lists/dash/msg00675.html

HTH, - 史蒂夫

答案 8 :(得分:0)

这对我有用,代码为/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将存在于内存中,如果你ps你可以看到它。