Linux下Python读写Named Pipes丢失文本行的问题排查与解决
命名管道(Named Pipes)进程间通信数据丢失问题排查与解决
我正在学习用命名管道实现进程间数据传递,写了两个Python脚本pipewriter.py和pipereader.py,通过mkfifo my_pipe创建了名为my_pipe的命名管道,所有文件均在同一目录下。
原脚本内容
pipewriter.py
#!/usr/bin/env python3 for i in range(1, 5): with open('my_pipe', 'a') as outfile: outfile.write(f'Hello {i}\n') if i==4: outfile.write('\n') print('Done')
pipereader.py
#!/usr/bin/env python3 while True: with open('my_pipe', 'r') as infile: input = infile.read() print(input,end="")
问题现象
- 单轮执行:在一个终端运行
./pipereader.py,另一个终端运行./pipewriter.py,输出完整:
Hello 1 Hello 2 Hello 3 Hello 4
- 多轮执行:运行
./pipewriter.py; ./pipewriter.py; ./pipewriter.py(pipereader.py在另一个终端持续运行),输出偶尔会丢失部分行,例如:
Hello 1 Hello 2 Hello 3 Hello 1 <- 丢失"Hello 4" Hello 2 Hello 3 Hello 4 Hello 1 Hello 2 Hello 3 Hello 4
尝试的修改
将pipewriter.py改为每次执行仅打开一次命名管道,似乎能避免丢失,但不确定是否只是降低了丢失频率而非彻底解决:
pipewriter.py(修改版)
#!/usr/bin/env python3 with open('my_pipe', 'a') as outfile: for i in range(1, 5): outfile.write(f'Hello {i}\n') outfile.write('\n') print('Done')
补充测试(带参数版本)
为更清晰定位问题,给pipewriter.py添加了命令行参数:
修改后的pipewriter.py(带参数)
#!/usr/bin/env python3 import sys for i in range(1, 5): with open('my_pipe', 'a') as outfile: outfile.write(f'Hello {sys.argv[1]} {i}\n') if i==4: outfile.write('\n') print('Done')
执行./pipewriter.py A; ./pipewriter.py B; ./pipewriter.py C的输出示例1:
Hello A 1 Hello A 2 Hello A 3 Hello A 4 Hello B 1 Hello B 2 Hello B 3 Hello B 4 Hello C 1 Hello C 2 Hello C 3
执行./pipewriter.py A; ./pipewriter.py B; ./pipewriter.py C的输出示例2:
Hello A 1 Hello A 2 Hello A 3 Hello A 4 Hello B 1 Hello B 2 Hello B 3 Hello B 4 Hello C 1 Hello C 2
问题原因分析
1. 命名管道的核心特性
命名管道是字节流而非消息队列,没有内置消息边界;同时,当所有打开管道的写端关闭时,读端的read()会返回EOF(空字符串)。
2. 原writer脚本的问题
原脚本在循环中每次打开、写入、关闭管道:
- 每次循环结束关闭写端时,读端的
infile.read()会收到EOF,返回已读取内容后退出当前with块,重新打开管道等待下一次写入。 - 多次打开/关闭的间隙,可能出现写端关闭后、读端还未重新打开管道的窗口,此时新写端的写入数据会被遗漏;或者writer快速多次关闭/打开时,reader的
read()可能在writer写入最后一行前就触发EOF,导致最后一行未被读取。
3. 修改后writer脚本的有效性
修改后的脚本全程保持管道写端打开,直到所有数据写入完成后才关闭:
- 整个writer进程生命周期内写端始终打开,reader的
read()会持续读取数据,直到writer关闭写端才收到EOF。 - 这种方式消除了多次打开/关闭管道带来的EOF触发时机问题,是彻底解决数据丢失,而非降低频率。
额外的reader脚本优化
原reader脚本在while True循环中每次打开管道,可优化为保持读端持续打开,避免重复打开的开销和潜在间隙:
优化后的pipereader.py
#!/usr/bin/env python3 with open('my_pipe', 'r') as infile: while True: data = infile.readline() # 按行读取更适配文本场景 if not data: # 收到EOF时继续等待,因为可能有新writer打开管道 continue print(data, end="")
内容的提问来源于stack exchange,提问作者Paul Wintz
相关产品推荐
相关产品推荐

