【发布时间】:2020-06-27 18:25:53
【问题描述】:
我有一个与this 问题非常相似的问题,我需要满足以下条件:
- 如果打开文件以供读取,则该文件只能被任何其他进程/程序打开以供读取
- 如果打开文件以进行写入,则该文件只能被任何其他进程/程序打开以进行读取
链接问题中发布的解决方案使用第三方库,该库将任意.LOCK 文件添加到与相关文件相同的目录中。这是一个仅适用于正在使用该库的程序的解决方案,并且不会阻止任何其他进程/程序使用该文件,因为它们可能无法用于检查 .LOCK 关联。
本质上,我希望仅使用 Python 的标准库来复制 this 结果。
BLUF:需要一个特定于 Windows 的标准库实现,用于独占文件锁定
举一个问题集的例子,假设有:
- 共享网络/驱动器上的 1 个文件
- 2 个用户在不同的进程/程序上
假设用户 1 正在文件上运行程序 A,并在某个时间点执行以下操作:
with open(fp, 'rb') as f:
while True:
chunk = f.read(10)
if chunk:
# do something with chunk
else:
break
因此他们一次遍历文件 10 个字节。
现在用户 2 稍后在同一个文件上运行程序 B:
with open(fp, 'wb') as f:
for b in data: # some byte array
f.write(b)
在 Windows 上,有问题的文件会立即被截断,程序 A 停止迭代(即使没有完成),程序 B 开始写入文件。因此,我需要一种方法来确保文件不会以其他模式打开,如果之前打开会改变其内容。
我在看msvcrt 库,即msvcrt.locking() 接口。我成功的做法是确保可以锁定打开以供阅读的文件,但没有其他人可以读取该文件(因为我锁定了整个文件):
>>> f1 = open(fp, 'rb')
>>> f2 = open(fp, 'rb')
>>> msvcrt.locking(f1.fileno(), msvcrt.LK_LOCK, os.stat(fp).st_size)
>>> next(f1)
b"\x00\x05'\n"
>>> next(f2)
PermissionError: [Errno 13] Permission denied
这是一个可以接受的结果,只是不是最想要的。
在同一场景中,用户 1 运行程序 A,其中包括:
with open(fp, 'rb') as f
msvcrt.locking(f.fileno(), msvcrt.LK_LOCK, os.stat(fp).st_size)
# repeat while block
msvcrt.locking(f.fileno(), msvcrt.LK_UNLCK, os.stat(fp).st_size)
然后用户 2 稍后运行程序 B,出现相同的结果并且文件被截断。
此时,我希望有一种方法向用户 2 抛出错误,说明文件已打开以在其他地方读取,此时无法写入。但是如果用户 3 过来打开文件进行阅读,那就没有问题了。
更新:
一个潜在的解决方案是更改文件的权限(如果文件已在使用中,则捕获异常):
>>> os.chmod(fp, stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH)
>>> with open(fp, 'wb') as f:
# do something
PermissionError: [Errno 13] Permission denied <fp>
这感觉不是最好的解决方案(特别是如果用户没有权限甚至更改权限)。仍在寻找适当的锁定解决方案,但msvcrt 不会阻止文件被锁定以供读取时截断和写入。似乎仍然没有办法使用 Python 的标准库生成独占锁。
【问题讨论】:
-
如果只是Windows,可以调用
CreateFile(如PyWin32的win32file.CreateFile),将共享模式设置为所需的读/执行、写/追加、删除/重命名共享。通过msvcrt.open_osfhandle用文件描述符包装它返回的文件句柄。然后通过open打开文件描述符。 -
@ErykSun 但这不是 Python 标准库实现吗?它需要 PyWin32。
-
标准库有 ctypes。假设您设置了函数原型并正确处理错误和异常以使其具有惯用性,那么使用 ctypes 实现它需要做更多的工作。
-
@ErykSun 是的,这就是我目前正在走的路。
-
@ErykSun 虽然它按预期工作(我将发布一个解决方案),但奇怪的是
CreateFileW不会抛出FileNotFoundError。如果路径不存在,则返回-1,然后msvcrt.open_osfhandle返回OSError: Bad file descriptor。根据 MSDN 文档,我原以为会引发前一个错误。
标签: python windows file locking