簡體   English   中英

Python 等到數據進入 sys.stdin

[英]Python wait until data is in sys.stdin

我的問題如下:

我的 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 使用率。

以下應該只是工作。

import sys
for line in sys.stdin:
    # whatever

理由:

代碼將在標准輸入中的行進入時對其進行迭代。如果 stream 仍然打開,但沒有完整的行,則循環將掛起,直到遇到換行符(並返回整行)或 stream關閉(並返回緩沖區中剩余的任何內容)。

一旦 stream 關閉,就不能再向標准輸入寫入或讀取數據。 時期。

您的代碼使您的 cpu 過載的原因是,一旦標准輸入被關閉,任何后續迭代標准輸入的嘗試都將立即返回而不做任何事情。 本質上,您的代碼等同於以下內容。

for line in sys.stdin:
    # do something

while 1:
    pass # infinite loop, very CPU intensive

如果您發布了如何將數據寫入標准輸入,也許會很有用。

編輯:

Python will (for the purposes of for loops, iterators and readlines() consider a stream closed when it encounters an EOF character. You can ask python to read more data after this, but you cannot use any of the previous methods. The python man頁面推薦使用

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

當遇到 EOF 字符時,readline 將返回一個空字符串。 如果 stream 仍處於打開狀態,則對 readline 的下一次調用將正常執行 function。 您可以通過在終端中運行命令來自行測試。 按 ctrl+D 將導致終端將 EOF 字符寫入標准輸入。 這將導致本文中的第一個程序終止,但最后一個程序將繼續讀取數據,直到 stream 實際關閉。 最后一個程序不應該 100% 占用你的 CPU,因為 readline 會等到有數據返回而不是返回一個空字符串。

當我從實際文件中嘗試 readline 時,我只會遇到繁忙循環的問題。 但是當從標准輸入讀取時,readline 會愉快地阻塞。

很長一段時間后,我又回到了問題上。 問題似乎是 Apache 將 CustomLog 視為文件——它可以打開、寫入、關閉,然后在以后重新打開。 這會導致接收進程被告知其輸入 stream 已關閉。 但是,這並不意味着進程輸入 ZF7B44CFFAFD5C52223D5498196C8A2E7BZ 不能再次寫入,只是無論哪個進程寫入輸入 stream 都不會再次寫入。

處理此問題的最佳方法是設置一個處理程序,並讓操作系統知道在將輸入寫入標准輸入時調用該處理程序。 通常,您應該避免嚴重依賴操作系統信號事件處理,因為它們相對昂貴。 但是,將 1 兆字節的文本復制到跟隨只產生兩個 SIGIO 事件,所以在這種情況下是可以的。

花式回聲.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()

你可以如何使用這個玩具程序。 由於輸入和 output 的交錯,Output 已被清理。

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

這實際上完美無缺(即沒有失控的 CPU) - 當您從 shell 調用腳本時,如下所示:

tail -f input-file | yourscript.py

顯然,這並不理想 - 因為您必須將所有相關的標准輸出寫入該文件 -

但它無需太多開銷即可工作! 即因為使用readline() - 我認為:

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

它實際上會在該行停止並等待,直到獲得更多輸入。

希望這對某人有幫助!

用這個:

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

好吧,我現在將堅持這些代碼行。

#!/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 解決的解決方案在每個周期都有固定的 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)

我遇到了類似的問題,其中 python 等待發送者(無論是用戶還是其他程序)在循環開始執行之前關閉 stream。 我已經解決了它,但它顯然不是pythonic,因為我不得不求助於while True:sys.stdin.readline()

我最終在另一篇文章的評論中找到了對名為io的模塊的引用,該模塊是標准文件 object 的替代品。 在 Python 3 中,這是默認設置。 據我所知,Python 2 將標准輸入視為普通文件,而不是 stream。

試試這個,它對我有用:

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

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

我知道這是一個舊線程,但我偶然發現了同樣的問題,並發現這更多地與腳本的調用方式有關,而不是腳本的問題。 至少在我的情況下,這被證明是 debian 上的“系統外殼”的問題(即:/bin/sh 鏈接到的內容——這是 apache 用來執行 CustomLog 管道到的命令的內容)。 更多信息在這里: http://www.spinics.net/lists/dash/msg00675.html

hth, - 史蒂夫

這對我有用,/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(),否則你可能看不到 output。 我在 fedora 15、python 2.7.1、Apache 2.2 上測試,不是 CPU 負載,alog.py 將存在於 memory 中,如果你能看到。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM