如何在进程终止后等待文件在 Windows 使用 Python 解锁?

How to wait after process kill until the file is unlocked on Windows using Python?

问题描述

我有一个调用外部程序并进行一些测试的单元测试。之后,外部进程被终止,测试尝试清理外部程序创建的文件。但是,如果我在 Windows 10 上的 kill() 命令之后直接调用 unlink(),我会得到:

PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'my.log'

如果我 time.sleep(4) 在调用 unlink() 之前一切正常。 4 随便选的,其他时间也行。

MCVE

这个 MCVE 有两个文件。 server.py 简单地锁定日志文件和 test_client.py 调用服务器,杀死它并最终尝试删除日志文件。

test_client.py

import pathlib
import subprocess
import sys
import time

# Create test folder
server_path = pathlib.Path('.')
server_path.mkdir(exist_ok=True)

server_file = pathlib.Path('server.py').resolve()

# Start server in test folder
proc = subprocess.Popen([sys.executable, str(server_file)], cwd=server_path)

time.sleep(4)

# Kill server
proc.kill()

# Activate the following line to avoid the PermissionError: [WinError 32] ...
#time.sleep(4)

# Clean up
pathlib.Path('my.log').unlink()

server.py

import time
import logging

logging.basicConfig(
    filename='my.log',
    level=logging.DEBUG)

logging.info('I just started my work')

while True:
    time.sleep(1)

问题

正确的方法是

# Kill server
proc.kill()

# Activate the following line to avoid the PermissionError: [WinError 32] ...
proc.communicate()

# Clean up
pathlib.Path('my.log').unlink()

为什么会这样,需要一些文档。

Python Official Documentation

所述

Popen.kill() Kills the child. On Posix OSs the function sends SIGKILL to the child. On Windows kill() is an alias for terminate().

Popen.terminate() Stop the child. On Posix OSs the method sends SIGTERM to the child. On Windows the Win32 API function TerminateProcess() is called to stop the child.

在 windows 中,如前所述,它在 Win32 API 上调用 TerminateProcess function。同时,明确指出

TerminateProcess is asynchronous; it initiates termination and returns immediately. If you need to be sure the process has terminated, call the WaitForSingleObject function with a handle to the process.

因此,kill()就是"Ask for stop"。还没有停止。因此,您需要一个 'waiting' 进程结束的方法。

communicate() 也是为此目的而设计的,如文档

中所述

Interact with process: Send data to stdin. Read data from stdout and stderr, until end-of-file is reached. Wait for process to terminate.