关闭前冲洗管道(os.pipe)

时间:2014-09-16 10:14:05

标签: python pipe ipc buffering

我需要启动一个子流程并启用两个线程来分别读取它stdoutstderr

以下代码正在考虑stdout

def reader(rfd):
    while True:
        try:
            data = os.read(rfd, bufsize)
        except OSError:
            break
        else:
            chomp(data)

rout, wout = os.pipe()
tout = threading.Thread(target=reader, args=(rout,))
tout.start()

subprocess.check_call(command, bufsize=bufsize, stdout=wout, stderr=werr)

os.close(wout)
os.close(rout)
tout.join()

代码有效,除了我注意到并非所有数据都被处理,好像{<1}}函数在读取所有数据之前杀死了阅读器。另一方面,如果我不关闭os.close(wout),我的流程将永远挂在wout上。

我可以说这是一个缓冲问题,因为如果我在tout.join()之后放置一个非常糟糕的time.sleep(0.1),一切都神奇地起作用。

好方法是刷新而不是等待,但通过管道对subprocess.check_call(...)的任何调用都会给os.fsync()

有关如何刷新使用OSError: [Errno 22] Invalid argument创建的管道的任何提示?

2 个答案:

答案 0 :(得分:2)

我建议使用Popen而不是os.pipe进行进程间通信。

例如

writer_process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
reader_thread = threading.Thread(target=reader, args=(writer_process.stdout,))
reader_thread.start()
reader_thread.join()

但是,如果您真的想使用os.pipe,那么您可以更轻松地将它们视为文件对象。 Python内置的文件上下文管理器将确保文件的正确刷新和关闭。

例如

def reader(fd):
    with os.fdopen(fd, bufsize=bufsize) as f:
        while True:
            data = f.read(bufsize)
            if not data:
                break
            chomp(data)

with os.fdopen(wout, "w", bufsize=bufsize) as f:
    subprocess.check_call(cmd, stdout=f)

答案 1 :(得分:1)

在确定进程已完成写入之前(因为您将丢失数据),您无法关闭管道,并且您无法等待线程完成而不关闭管道(因为{{1}将永远阻止)。

您需要等待进程完成,并手动关闭管道的写入端(因为您创建了它)。

这是一个自包含的例子:

os.read