防止在多进程库中文件句柄继承

13 投票
5 回答
4538 浏览
提问于 2025-04-15 12:02

在Windows上使用多进程时,似乎任何打开的文件句柄都会被新创建的进程继承。这会导致一个不太好的后果,就是这些文件会被锁定。

我想知道有没有办法:
1) 防止这种继承
2) 找到一种方法来释放被新进程占用的文件

看看下面这段代码,在OSX上运行得很好,但在Windows上执行到os.rename时就会崩溃。

from multiprocessing import Process
import os

kFileA = "a.txt"
kFileB = "b.txt"

def emptyProcess():
    while 1:
        pass

def main():
    # Open a file and write a message
    testFile = open(kFileA, 'a')
    testFile.write("Message One\n")

    # Spawn a process
    p = Process(target=emptyProcess)
    p.start()

    # Close the file
    testFile.close()

    # This will crash
    # WindowsError: [Error 32] The process cannot access the file
    #               because it is being used by another process
    os.rename(kFileA, kFileB)

    testFile = open(kFileA, 'a')
    testFile.write("Message Two\n")
    testFile.close()

    p.terminate()


if __name__ == "__main__":
    main()

5 个回答

0

在你打开一个文件的句柄之后,可以使用 SetHandleInformation() 这个函数来去掉 HANDLE_FLAG_INHERIT 这个标志。

3

我对 multiprocessing 模块不太了解,不过使用 subprocess 模块时,你可以告诉它不要继承任何文件描述符:

如果设置 close_fds 为真,那么在执行子进程之前,除了 0、1 和 2 这三个文件描述符外,其他的都会被关闭。(仅限 Unix 系统)。在 Windows 系统上,如果 close_fds 为真,那么子进程不会继承任何句柄。需要注意的是,在 Windows 上,你不能同时将 close_fds 设置为真,并通过设置 stdinstdoutstderr 来重定向标准句柄。

另外,你也可以在子进程中使用 os.closerange 来关闭所有文件描述符:

关闭从 fd_low(包含)到 fd_high(不包含)之间的所有文件描述符,忽略错误。适用系统:Unix 和 Windows。

5

fileno() 方法会返回一个文件号,这个文件号是由运行时库分配的。拿到这个文件号后,你可以调用 msvcrt.get_osfhandle() 来获取 Win32 的文件句柄。然后,你可以用这个句柄去调用 SetHandleInformation。所以,像下面这样的代码可能会有效:

win32api.SetHandleInformation(
    msvcrt.get_osfhandle(testFile.fileno()),
    win32api.HANDLE_FLAG_INHERIT,
    0)

我对 win32api 模块的具体用法不是很确定,但这应该能帮助你理解 Python 文件对象和 Win32 句柄之间的关系。

撰写回答