Python多进程Connection的recv()为何阻塞且不抛出EOFError?
你的代码在c2.recv()处阻塞却未抛出EOFError,核心是两个因素叠加导致:
竞态条件让send操作“抢先”完成
子进程启动后,需要一点时间执行conn.close()并退出,但主进程在p.start()后立刻调用c2.send(123)——这时候子进程大概率还没来得及关闭c1,所以send成功把数据写入了管道缓冲区,后续这些数据会因为c1关闭被丢弃,但这个成功的send会干扰管道的状态判断。双向管道的EOF检测逻辑
multiprocessing的Pipe是双向的:c2.send()的数据只能由c1.recv()接收,c2.recv()则是等待c1.send()发来的数据。当子进程关闭c1后,c1的读写端都被销毁,但主进程这边因为之前成功执行了send,管道的读端(c2用来接收c1数据的那一端)不会立刻检测到EOF——操作系统会认为管道还有未处理的写入操作,因此c2.recv()会一直阻塞,直到明确收到另一端的关闭信号。
另外,c2.poll(5)返回False只是说明当前没有可接收的数据,不等于检测到另一端关闭。只有当读端完全无数据,且确认另一端已经关闭时,recv()才会抛出EOFError。
验证与修复
如果在send前等待子进程完成c1.close(),就能复现文档描述的EOFError:
from multiprocessing import Pipe, Process import time def f(conn): conn.close() if __name__ == "__main__": c1, c2 = Pipe() p = Process(target=f, args=(c1,)) p.start() time.sleep(1) # 等待子进程关闭c1并退出 print(111) try: c2.send(123) except BrokenPipeError: print("发送失败:另一端已关闭") print(222) print(c2.poll(1)) print(333) try: c2.recv() except EOFError: print("触发EOFError:另一端已关闭且无数据可接收") print(444) p.join()
运行后会输出:
111 发送失败:另一端已关闭 222 False 333 触发EOFError:另一端已关闭且无数据可接收 444
总结
你的代码未触发EOFError,是因为主进程的send操作抢在子进程关闭c1前完成,干扰了管道的EOF检测逻辑。只有当另一端明确关闭,且当前端没有未完成的写入操作、也无待接收数据时,recv()才会抛出EOFError。
内容的提问来源于stack exchange,提问作者Nicolás Tsu

