Python中按行读取管道文本时如何检测管道已关闭
Python中按行读取管道文本时如何检测管道已关闭
我太懂你这种头疼的情况了——明明管道的写入端已经关闭,读取线程的循环却死活不退出,thread.join()一直卡着,只能靠Ctrl+C强制中断。咱们来拆解问题,把它解决掉。
问题根源分析
你当前代码的核心问题是没正确处理readline()返回空字符串的情况:当管道写入端关闭后,readline()会返回空字符串(这是Python表示EOF的标准方式),但你的循环里只在chunk非空时做处理,空字符串的情况直接跳过了,导致循环永远不会终止。
另外,你用r_stream.closed来判断是否退出是没用的——读取端的文件对象不会因为写入端关闭而自动关闭,这个条件永远为False,所以那个break永远触发不了。
修正方案
我们只需要在读取到空字符串时,立即跳出循环即可。同时可以简化掉一些没必要的select逻辑(如果你的场景不需要非阻塞检测的话),下面是修正后的完整代码:
from datetime import datetime from itertools import batched import os from threading import Thread from time import sleep # Each superscript digit encodes to 3 bytes. text = '⁰¹²\n³\n⁴\n⁵⁶\n⁷⁸⁹⁰¹²\n³' text_bytes = bytes(text, 'utf8') pipe_r, pipe_w = os.pipe() wb_stream = open(pipe_w, 'wb', buffering=0) r_stream = open(pipe_r, 'r') got_chunks = [] def read_loop(): prev_timestamp = datetime.now() while True: sleep(1) cur_timestamp = datetime.now() print('A', (cur_timestamp - prev_timestamp).total_seconds()) prev_timestamp = cur_timestamp # 关键:readline()返回空字符串表示EOF(写入端已关闭) chunk = r_stream.readline() cur_timestamp = datetime.now() print('B', (cur_timestamp - prev_timestamp).total_seconds()) prev_timestamp = cur_timestamp if chunk: print('got chunk', repr(chunk)) got_chunks.append(chunk) else: # 空字符串说明管道已关闭,退出循环 print('Got EOF, exiting read loop') break thread = Thread(target=read_loop) thread.start() for batch in batched(text_bytes, 4): bytes_chunk = bytes(batch) sleep(1.6) wb_stream.write(bytes_chunk) wb_stream.close() thread.join() print('Final received chunks:', repr(got_chunks))
关键修改点说明
- 检测
readline()的空返回值:这是Python中判断文本流EOF的标准方式,当写入端关闭后,readline()会立即返回空字符串,此时我们直接跳出循环。 - 移除无效的
select和r_stream.closed判断:这些逻辑对你的场景来说没必要,反而干扰了正确的EOF检测。如果确实需要非阻塞读取,可以考虑将文件对象设置为非阻塞模式,但对于按行读取的场景,利用readline()的返回值已经足够简单可靠。
运行效果
修正后的代码运行后,读取循环会在写入端关闭并读完所有数据后,检测到EOF并退出,thread.join()也会正常结束,不会再阻塞。你会看到类似这样的输出(最后会打印Got EOF, exiting read loop,然后输出最终的got_chunks):
A 1.000234 B 0.0 got chunk '⁰¹²\n' A 1.000567 B 0.0 got chunk '³\n' ... A 1.000123 B 0.0 got chunk '³' A 1.000456 B 0.0 Got EOF, exiting read loop Final received chunks: ['⁰¹²\n', '³\n', '⁴\n', '⁵⁶\n', '⁷⁸⁹⁰¹²\n', '³']
这样你的程序就能正常退出,不用再靠Ctrl+C打断了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

