从 popen 句柄读取的低开销方法



我继承了进入繁忙循环的代码,读取子进程的输出以查找关键字,但我希望它以较低的开销工作。代码如下:

def stdout_search(self, file, keyword)
    s = ''
    while True:
        c = file.read(1)
        if not c:
            return None
        if c != 'r' and c != 'n':
            s += c
            continue
        s = s.strip()
        if keyword in s:
            break
        s = ''
    i = s.find(keyword) + len(keyword)
    return s[i:]
def scan_output(self, file, ev)
    while not ev.wait(0):
        s = self.stdout_search(file, 'Keyword:')
        if not s:
            break
        # Do something useful with s
        offset = #calculate offset
        wx.CallAfter(self.offset_label.SetLabel offset)
        #time.sleep(0.03)

Popen ed 过程的输出如下所示:

Keyword: 1 of 100
Keyword: 2 of 100
...etc...

scan_output结束时取消注释time.sleep(0.03)会使单个内核上的负载从 100% 降低到可接受的 25% 左右,但不幸的是偏移标签重绘卡顿,虽然我正在从 30 fps 播放中读取帧数,但标签通常每秒更新不到一次。如何通过更正确的等待输入来实现此代码?

顺便说一句,完整的代码可以在这里找到。

一次读取一个字节是低效的。请参阅在 Python 中读取二进制文件并遍历每个字节。

如果您不需要即时反馈;请使用Popen.communicate()一次获取所有输出。

为避免冻结 GUI,您可以将 IO 放入后台线程。它是支持增量读取的阻塞 IO 的简单便携式选项。

为了在输出被子进程刷新后立即处理输出,您可以使用异步 I/O,例如 Tkinter 的 createfilehandler()、Gtk 的 io_add_watch() 等 - 您提供一个回调,GUI 会在下一个数据块准备就绪时调用它。

如果孩子太频繁地刷新数据;回调可能只是读取块并将其放入缓冲区中,那么您可以使用 Tkinter 的 widget.after()、Gtk 的GObject.timeout_add()或每当它达到特定大小或行上的特定数字等时,每 X 秒处理一次缓冲区。

要读取到 'Keyword:' ,您可以使用类似于 asyncio readuntil()的代码。另请参阅,如何在 python 中从文件中读取由自定义分隔符终止的记录?

最新更新