Предотвращение наследования дескриптора файла в многопроцессорной библиотеке lib

Использование многопроцессорной обработки в окнах, похоже, что все открытые дескрипторы файлов наследуются порожденными процессами. Это имеет неприятный побочный эффект от их блокировки.

Меня интересует:
1) Предотвращение наследования
2) Способ освобождения файла из порожденного процесса

Рассмотрим следующий код, который отлично работает на OSX, но падает на окна в 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()

Ответ 1

Метод fileno() возвращает номер файла, назначенный библиотекой времени выполнения. После номера файла вы можете вызвать msvcrt.get_osfhandle(), чтобы получить дескриптор файла Win32. Используйте этот дескриптор в вызове SetHandleInformation. Возможно, что-то вроде следующего:

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

Я не уверен в точном использовании модуля win32api, но это должно помочь устранить разрыв между файловым объектом Python и дескриптором Win32.

Ответ 2

Я не знаю о модуле многопроцессорности, но с модулем subprocess вы можете поручить ему не наследовать никаких файловых дескрипторов

Если значение close_fds истинно, все дескрипторы файлов, кроме 0, 1 и 2, будут закрыты до выполнения дочернего процесса. (Только для Unix). Или, в Windows, если close_fds истинно, тогда никакие дескрипторы не будут унаследованы дочерним процессом. Обратите внимание, что в Windows вы не можете установить close_fds в true, а также перенаправить стандартные дескрипторы, установив stdin, stdout или stderr.

В качестве альтернативы вы можете закрыть все дескрипторы файлов в дочернем процессе с помощью os.closerange

Закройте все дескрипторы файлов из fd_low (включительно) в fd_high (исключая), игнорируя ошибки. Доступность: Unix, Windows.

Ответ 3

После открытия дескриптора файла вы можете использовать функцию SetHandleInformation(), чтобы удалить флаг HANDLE_FLAG_INHERIT.

Ответ 4

Я столкнулся с этой проблемой при использовании вращающегося журнала и многопроцессорности. Когда родительский процесс пытается повернуть журнал, он терпит неудачу с помощью

WindowsError: [Ошибка 32] Процесс не может получить доступ к файлу, потому что он используется другим процессом

основанный на некоторых других ответах, следующее рабочее решение в python 2.7 для предотвращения наследования обработчиков файлов журнала

fd = logging.getLogger().handlers[0].stream.fileno() # The log handler file descriptor
fh = msvcrt.get_osfhandle(fd) # The actual windows handler
win32api.SetHandleInformation(fh, win32con.HANDLE_FLAG_INHERIT, 0) # Disable inheritance

Обратите внимание, что этот вопрос был несколько адресован в python 3.4. для получения дополнительной информации см. https://www.python.org/dev/peps/pep-0446/