Python 如何询问管道有多少字节可供读取?
我已经用Python实现了一个非阻塞读取器,我需要提高它的效率 背景:我需要从一个子进程(从Popen()开始)读取大量的输出并传递给另一个线程。从该子进程读取输出的阻塞时间不得超过几毫秒(最好是读取可用字节所需的最短时间) 目前,我有一个实用程序类,它接受一个文件描述符(stdout)和一个超时。我Python 如何询问管道有多少字节可供读取?,python,subprocess,popen,nonblocking,Python,Subprocess,Popen,Nonblocking,我已经用Python实现了一个非阻塞读取器,我需要提高它的效率 背景:我需要从一个子进程(从Popen()开始)读取大量的输出并传递给另一个线程。从该子进程读取输出的阻塞时间不得超过几毫秒(最好是读取可用字节所需的最短时间) 目前,我有一个实用程序类,它接受一个文件描述符(stdout)和一个超时。我select()和readline(1)直到发生以下三种情况之一: 我读了一条新行 我的超时(几毫秒)过期 select告诉我该文件描述符上没有可读取的内容 然后我将缓冲文本返回给调用方法,调用方法
select()
和readline(1)
直到发生以下三种情况之一:
readline([那么多字节])
。它应该只是传递一些信息,所以我不在乎新行在哪里,甚至不在乎是否有新行我可以询问文件描述符有多少字节可供读取,如果可以,如何读取?
我做了一些搜索,但我真的很难弄清楚要搜索什么,更不用说是否可能了
哪怕是正确方向上的一点都会有帮助
注意:我是在Linux上开发的,但这对于“Pythonic”解决方案来说并不重要。在Linux上,os.pipe()
只是管道的包装器(2)。两者都返回一对文件描述符。通常使用lseek(2)(os.lseek()
在Python中)重新定位文件decsriptor的偏移量,作为获取可用数据量的一种方法。但是,并不是所有的文件描述符都能够查找
在Linux上,在管道上尝试lseek(2)将返回错误,请参阅。这是因为管道或多或少是数据生产者和消费者之间的缓冲区。缓冲区的大小取决于系统
在Linux上,这是您可以获得的最多数据
<>强>编辑< /强>:如果你可以改变子进程的工作方式,你可以考虑使用内存映射文件,或者是一个很好的共享内存块。
Edit2:使用可能比选择更快。这个问题似乎提供了一个可能的解决方案,但可能需要重新设置工具 否则,我假设您知道如何一次读取N个字节的数据:
all_data = ''
while True:
data = pipe.read(1024) # Reads 1024 bytes or to end of pipe
if not data:
break
all_data += data
# Add your timeout break here
您可以通过调用os.fstat(file_descriptor)并检查st_size属性(即写入的字节数)来找到这一点
import os
reader_file_descriptor, writer_file_descriptor = os.pipe()
os.write(writer_file_descriptor, b'I am some data')
readable_bytes = os.fstat(writer_file_descriptor).st_size
下面是一个您应该了解的实用程序:因此,即使我的类通常能够使用lseek()来计算文件描述符中可用的字符数,但如果我向它传递一个stdout管道(来自Popen()),我也无法这样做,对吗?我是否理解正确?有没有其他方法可以让我询问管道等待了多少字节,或者lseek()是完成这类事情的唯一方法?好吧,lseek基本上是唯一的方法。不会
pipe.read(1024)
阻塞,直到它得到1024字节或抛出异常(如查找EOF)?另一个问题中的方法似乎很有趣,但不能解决我的特定问题。我有一个非阻塞阅读器,可以工作;我需要知道我是否可以用这种方式进行非阻塞读取;-)是的,它将阻塞直到读取1024字节。您可以将1024设置为任意小,以使其极不可能(但不能保证)超过超时限制。但是你的读线(1)也被阻塞了,对吗?虽然规模较小。是的,这正是我试图解决的问题。因为要读取的字节数无法预测,所以无法选择完美的字节限制。我可以选择一个字节限制,它平均不需要多次阻塞,也不需要多次重复,但这两种情况都是不可取的。我宁愿准确地读取等待的字节数,既不阻塞,也不必返回获取其余的字节。但这似乎是不可能的…@mHurley:fyi,os.read(fd,8096)
可能返回小于8096
字节,即,一个简单的select()
(避免在超时秒内没有可用字节时阻塞)+os.read()
(在select()
之后获取可用数据)可能就足够了。您可以测试epoll()
是否在您的情况下产生更好的结果,并使用os.read()
尝试不同的buffersize
s(越大越好)。