【问题标题】:Python wait until data is in sys.stdinPython 等到数据在 sys.stdin 中
【发布时间】:2011-10-26 17:52:48
【问题描述】:

我的问题如下:

我的 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 会说 ohh 你的脚本意外退出。

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

【问题讨论】:

  • 听起来你的问题出在其他地方。在 python 脚本中,stdin 中是否有数据并不重要,只要它是打开的即可。写入 python 脚本的任何内容都会过早关闭流。
  • 首先,您应该了解readline()readlines() 之间的区别。 readlines() 将从标准输入读取所有输入,直到 EOF(基本上调用 read() 然后用换行符分割)。这意味着它将在标准输入关闭时第一次返回。未来在标准输入上对readlines()(或read()readline())的调用将返回[](或"" 用于读取/读取行)。推荐阅读:docs.python.org/2/tutorial/inputoutput.htmlunix.stackexchange.com/questions/103885/…

标签: python wait


【解决方案1】:

使用这个:

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

【讨论】:

  • 如果我使用你的代码,如果没有数据,脚本就会结束。但是我的脚本需要等到新数据进来。
  • 没有。 for 循环将挂起等待更多数据。当标准输入关闭时,循环将结束,脚本将继续执行。
  • 不,在我的情况下它不起作用。我的脚本需要在 perl #!/usr/bin/perl $| 中表现得像这段代码。 = 1; while () { # ...在这里放任何转换或查找... print $_; }
  • 尝试解决方案,如果有问题再回来。
  • 其实,在这种情况下,你们都错了。 Abalus:当 stdin 中没有数据时,脚本不会结束,它会在 stdin 关闭时结束(尽管它仍然不适合你)。 @hamstergene: sys.stdin.readlines() 不会在找到行时产生行,但只有在收到 ctrl-d/EOF 时才会产生。
【解决方案2】:

以下应该可以正常工作。

import sys
for line in sys.stdin:
    # whatever

理由:

代码将在 stdin 中的行进入时对其进行迭代。如果流仍然打开,但没有完整的行,则循环将挂起,直到遇到换行符(并返回整行)或者流被关闭(并且返回缓冲区中剩下的任何内容)。

一旦流被关闭,就不能再向标准输入写入或读取数据。期间。

您的代码使您的 cpu 过载的原因是,一旦标准输入被关闭,任何后续迭代标准输入的尝试都将立即返回而无需执行任何操作。本质上,您的代码等同于以下代码。

for line in sys.stdin:
    # do something

while 1:
    pass # infinite loop, very CPU intensive

如果您发布了如何将数据写入标准输入,也许会很有用。

编辑:

Python 将(出于 for 循环、迭代器和 readlines() 的目的)在遇到 EOF 字符时认为流已关闭。在此之后您可以要求 Python 读取更多数据,但您不能使用之前的任何方法。 python手册页推荐使用

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

当遇到 EOF 字符时,readline 将返回一个空字符串。如果流仍然打开,对 readline 的下一次调用将正常运行。您可以通过在终端中运行命令来自行测试。按 ctrl+D 将导致终端将 EOF 字符写入标准输入。这将导致本文中的第一个程序终止,但最后一个程序将继续读取数据,直到流真正关闭。最后一个程序不应该 100% 占用你的 CPU,因为 readline 会等到有数据返回而不是返回一个空字符串。

当我尝试从实际文件中读取行时,我只会遇到繁忙循环的问题。但是从标准输入读取时,readline 会愉快地阻塞。

【讨论】:

  • 是的,我也考虑过使用普通日志并使用 open 打开它,然后使用 seek 和 tell 只获取其中的新行,但是在 perl 中它可以工作。所以我想知道如何在 python 中做到这一点。
  • 程序读取输入时不存在“EOF 字符”之类的东西。操作系统截获 ^D 并关闭程序的标准输入,但程序永远不会看到 ^D。要查看此内容,请在提示符处输入 cat | wc 并立即输入 ^D:您将向 wc 发送 0 个字符。
  • 虽然您是正确的,没有文字 EOF 字符进入程序的缓冲区,但断言流已关闭是错误的。输入 Ctrl-D 后,下一次调用 read 的底层实现(C 的 read)将返回 EOF 宏。随后的 read 调用将阻塞,直到更多数据进入缓冲区。所以流永远不会真正关闭,它使用通知程序另一端的任何东西都表示它打算停止发送数据。
  • @Dunes:在我的系统上(Ubuntu,屏幕)。 sys.stdin.readline() 在我输入 Ctrl+D 后仅返回空字符串(意思是 EOF),即使我尝试在 while True 循环中提供进一步的输入
  • @J.F.Sebastian 仍然为我工作。我在 virtualbox 上运行 Ubuntu 14。也许你可以尝试写/proc/<pid>/fd/0。你的过程会发生什么?您是否得到一个繁忙的循环,或者它只是挂起?
【解决方案3】:

好吧,我现在将坚持这些代码行。

#!/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的性能真的很差 非常感谢您的回答。

最好的问候 阿巴鲁斯

【讨论】:

    【解决方案4】:

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

    hth, - 史蒂夫

    【讨论】:

      【解决方案5】:

      这对我有用,/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可以看到。

      【讨论】:

        【解决方案6】:

        我知道我正在将旧东西带入生活,但这似乎是该主题的热门话题之一。 Abalus 解决的解决方案在每个周期都有固定的 time.sleep,不管标准输入实际上是空的,程序应该是空闲的还是有很多行等待处理。一个小的修改使程序可以快速处理所有消息,并且仅在队列实际上为空时才等待。所以只有在休眠期间到达的一行可以等待,其他的被处理没有任何延迟。

        这个例子只是简单地反转输入行,如果你只提交一行它会在一秒钟内响应(或任何你设置的睡眠时间),但也可以非常快速地处理像“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)
        

        【讨论】:

        • 只需删除 [::-1] 即可回显标准输入,而不是反向标准输入
        【解决方案7】:

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

        我最终在另一个 post 的评论中找到了对名为 io 的模块的引用,该模块是标准文件对象的替代品。在 Python 3 中,这是默认设置。据我所知,Python 2 将标准输入视为普通文件而不是流。

        试试这个,它对我有用:

        sys.stdin = io.open(sys.stdin.fileno())  # default is line buffering, good for user input
        
        for line in sys.stdin:
            # Do stuff with line
        

        【讨论】:

          【解决方案8】:

          很久以后我又遇到了问题。问题似乎是 Apache 将 CustomLog 视为文件——它可以打开、写入、关闭,然后在以后重新打开。这会导致接收进程被告知其输入流已关闭。然而,这并不意味着进程的输入流不能被再次写入,只是无论哪个进程写入输入流都不会再次写入。

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

          【讨论】:

            【解决方案9】:

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

            tail -f input-file | yourscript.py

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

            但它可以在没有太多开销的情况下工作! 即因为使用readline() - 我认为:

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

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

            希望这对某人有所帮助!

            【讨论】:

              猜你喜欢
              • 2016-11-20
              • 2017-06-15
              • 2016-03-30
              • 2016-04-25
              • 2013-11-17
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-08-15
              相关资源
              最近更新 更多