Python多进程spawn模式下共享os.open创建的文件描述符的问题
Python多进程spawn模式下共享os.open创建的文件描述符的问题
嗨,我来帮你搞定这个困扰!你遇到的问题完全是spawn模式和fork模式的核心差异导致的,咱们一步步拆解原因和解决方案:
首先,为什么POSIX系统(默认fork模式)没问题,Windows(默认spawn模式)就不行?
- fork模式下,子进程会直接复制父进程的整个地址空间和文件描述符表,所以父进程的fd在子进程里直接能用;
- 但spawn模式不一样,它会重新启动一个全新的Python解释器,导入你的代码后再执行目标函数,父进程的文件描述符不会自动带过去——哪怕你设了
os.set_inheritable也没用,因为multiprocessing默认不会主动传递这些“非必要”的资源(文档里说的“必要资源”指的是它内部通信用的管道之类的,不是咱们自己的文件描述符)。
那该怎么解决呢?咱们需要显式地把文件描述符传递给子进程,这里可以用multiprocessing.reduction模块里的工具来实现,它专门用来处理这种跨进程的资源传递。
给你改好的代码,直接就能在Windows的spawn模式下运行:
import multiprocessing import os from multiprocessing.reduction import send_handle, recv_handle import sys def read_file(conn): # 从管道接收父进程传来的文件描述符 fd = recv_handle(conn) try: print(os.read(fd, 1024)) finally: os.close(fd) conn.close() if __name__ == '__main__': # 先准备测试文件 with open('test.txt', 'wb') as fp: fp.write(b'hello world') fd = os.open('test.txt', os.O_RDONLY) try: # 标记fd为可继承,这一步不能少 os.set_inheritable(fd, True) # 创建管道用来传递fd parent_conn, child_conn = multiprocessing.Pipe() p = multiprocessing.Process(target=read_file, args=(child_conn,)) p.start() # 把fd通过管道传给子进程 send_handle(parent_conn, fd, sys.stdout.fileno()) p.join() finally: os.close(fd)
运行这个代码,你就能看到输出b'hello world'啦!
另外再补充几个关键点:
- 一定要调用
os.set_inheritable(fd, True),否则send_handle也没法传递这个fd; - 传递完成后记得在父子进程里都关闭fd,避免资源泄漏;
- 如果你用的是Python的文件对象(不是os.open返回的fd),其实也可以用类似的方式传递,不过直接用文件对象的话,在spawn模式下可能会有pickle的问题,所以用fd+reduction的方式更可靠。
备注:内容来源于stack exchange,提问作者Alex




