我正在尝试编写一个"cleaner"程序来释放一个在命名管道中被阻塞的潜在编写器(因为没有读取器从管道中读取) . 但是,当没有写入器被阻止写入管道时,清理器本身 should not 阻塞 . 换句话说,"cleaner"必须立即返回/终止,无论是否有被阻止的作者 .
因此,我搜索了“从命名管道读取Python非阻塞”,并得到了这些:
似乎他们建议只使用 os.open(file_name, os.O_RDONLY | os.O_NONBLOCK)
应该没问题,这并没有弄清楚自己出了什么问题 .
我找到了Linux手册页(http://man7.org/linux/man-pages/man2/open.2.html),O_NONBLOCK的解释似乎与他们的建议一致,但与我在机器上的观察不一致...
如果它是相关的,我的操作系统是 Ubuntu 14.04 LTS 64-bit .
这是我的代码:
import os
import errno
BUFFER_SIZE = 65536
ph = None
try:
ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
os.read(ph, BUFFER_SIZE)
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
raise err
else:
raise err
finally:
if ph:
os.close(ph)
(不知道如何做Python语法高亮...)
最初只有第二个 raise
,但我发现 os.open
和 os.read
虽然没有阻塞,但是真的知道作者会写多少到缓冲区!如果非阻塞 read
没有引发异常,我怎么知道何时停止读取?
2016年8月8日更新:
这似乎是满足我需求的解决方法/解决方案:
import os
import errno
BUFFER_SIZE = 65536
ph = None
try:
ph = os.open("pipe.fifo", os.O_RDONLY | os.O_NONBLOCK)
while True:
buffer = os.read(ph, BUFFER_SIZE)
if len(buffer) < BUFFER_SIZE:
break
except OSError as err:
if err.errno == errno.EAGAIN or err.errno == errno.EWOULDBLOCK:
pass # It is supposed to raise one of these exceptions
else:
raise err
finally:
if ph:
os.close(ph)
它将循环 read
. 每次读取内容时,它会将读取的内容大小与指定的 BUFFER_SIZE
进行比较,直到达到EOF(然后编写器将解锁并继续/退出) .
我仍然想知道为什么在 read
中没有引发异常 .
2016年10月8日更新:
为了说清楚,我的总体目标是这样的 .
我的主程序(Python)有一个线程作为读者 . 它通常在命名管道上阻塞,等待“命令” . 有一个编写器程序(Shell脚本),它将在每次运行中向同一个管道写一个单行“命令” .
在某些情况下,作者在主程序启动之前或主程序终止之后启动 . 在这种情况下,编写器将阻塞等待读者的管道 . 这样,如果稍后我的主程序启动,它将立即从管道中读取以从被阻止的编写器获得“命令” - 这不是我想要的 . 我希望我的程序忽略在它之前开始的作家 .
因此,我的解决方案是,在我的读者线程初始化期间,我执行非阻塞读取以释放编写器,而不是真正执行他们试图写入管道的“命令” .
1 回答
此解决方案不正确 .
这实际上并不会读取所有内容,只有在读取部分内容后才能读取 . 请记住:您只能保证使用常规文件填充缓冲区,在所有其他情况下,可以在EOF之前获取部分缓冲区 . 执行此操作的正确方法是循环,直到达到文件的实际结尾,这将给出长度为0的读取 . 文件结尾表示没有编写器(它们都已退出或关闭了fifo) .
但是,面对非阻塞IO,这将无法正常工作 . 事实证明,这里完全不需要非阻塞IO .
导致此代码阻止的唯一方案是,如果有一个活动的编写器已打开fifo但未写入它 . 从你所描述的情况来看,听起来并非如此 .
非阻塞IO不会这样做
你的程序想要做两件事,具体取决于具体情况:
如果没有作家,请立即返回 .
如果有写入器,则从FIFO读取数据直到写入器完成 .
非阻塞
read()
对任务#1没有任何影响 . 无论您是否使用O_NONBLOCK
,read()
将立即返回情况#1 . 所以唯一的区别在于情况#2 .在情况#2中,您的程序的目标是从编写器中读取整个数据块 . 这正是阻止IO工作的方式:它等待编写器完成,然后
read()
返回 . 如果操作可以't complete immediately, which is the opposite of your program'的目标 - 即等待操作完成,则非阻塞IO的重点是提前返回 .如果您使用非阻塞
read()
,在情况#2中,您的程序有时会在作者完成作业之前提前返回 . 或者也许你的程序会在从FIFO读取一半命令后返回,而另一半(现已损坏)则在那里 . 您的问题表达了这种担忧:您知道何时停止读取,因为
read()
在所有写入器关闭管道时返回零字节 . (方便的是,如果首先没有编写者,也会发生这种情况 . )遗憾的是,如果编写者在完成时没有关闭管道的末尾会发生这种情况 . 如果编写器在完成时关闭管道,则更简单,更直接,因此这是推荐的解决方案,即使您需要稍微修改编写器 . 如果作者无论出于何种原因都无法关闭管道,那么解决方案就更复杂了 .非阻塞
read()
的主要用例是,如果您的程序还有其他任务需要完成,而IO在后台继续运行 .