flock在Linux/macOS上是最轻量的内核级建议性独占锁,需用os.open获取fd后调用,进程退出或fd关闭时自动释放;Windows不支持,应改用win32file.CreateFile或跨平台portalocker。

Python中用flock实现独占文件锁(Linux/macOS)
在类Unix系统上,flock是最轻量、最常用的文件独占锁机制。它基于内核的 advisory lock,进程退出或文件描述符关闭时自动释放,无需手动清理。
关键点:必须用os.open打开文件获取原始文件描述符,再传给flock;不能对open()返回的file对象直接调用——那会报TypeError: argument must be an int。
- 使用
os.O_RDWR | os.O_CREAT确保文件可读写且不存在时自动创建 - 加锁失败时默认阻塞,如需非阻塞,传
fcntl.LOCK_EX | fcntl.LOCK_NB,捕获OSError(错误码errno.EAGAIN或errno.EACCES) - 锁作用于整个文件,不支持区域锁;多个进程对同一文件描述符重复调用
flock不会出错,但必须配对unlock或靠关闭fd释放
import fcntl
import os
<p>fd = os.open("data.txt", os.O_RDWR | os.O_CREAT)
try:
fcntl.flock(fd, fcntl.LOCK_EX)</p><h1>此处安全读写</h1><pre class="brush:php;toolbar:false;">with os.fdopen(fd, "r+") as f:
f.write("updated\n")
f.seek(0)
print(f.read())finally: fcntl.flock(fd, fcntl.LOCK_UN) # 显式解锁更稳妥 os.close(fd)
Windows下用msvcrt.locking或win32file
Windows没有flock,原生方案是msvcrt.locking,但它只能锁定文件的**字节范围**,且仅对已存在的文件有效,无法创建新文件并加锁——这和常见需求错位。
更可靠的做法是用pywin32的win32file模块,调用CreateFile以win32file.FILE_SHARE_NONE打开,实现真正的独占打开(open-time exclusive):
- 如果文件正被其他进程以非共享模式打开,
CreateFile会直接失败(抛pywintypes.error),无需额外加锁逻辑 - 该方式锁粒度是“整个文件句柄”,比
flock更严格,也更难被绕过 - 注意:必须用
win32file.CloseHandle关闭句柄,不能用close()或file.close()
import win32file import win32event <p>try: h = win32file.CreateFile( "data.txt", win32file.GENERIC_READ | win32file.GENERIC_WRITE, 0, # 不共享 None, win32file.OPEN_ALWAYS, 0, None )</p><h1>成功即已独占,可安全操作</h1><pre class="brush:php;toolbar:false;">win32file.WriteFile(h, b"hello") win32file.CloseHandle(h)
except win32file.pywintypes.error as e: if e.winerror == 32: # ERROR_SHARING_VIOLATION print("文件正被占用")
跨平台方案:用portalocker库统一处理
自己写平台判断容易漏掉边界情况(比如macOS的flock语义差异、Windows的句柄继承问题)。portalocker封装了这些细节,提供一致API。
它内部对Linux/macOS用flock,Windows用LockFileEx(支持超时、可重入等),还处理了fork后子进程继承锁的问题。
- 安装:
pip install portalocker -
portalocker.lock默认阻塞,portalocker.unlock显式释放;也可用上下文管理器自动释放 - 若需带超时,用
portalocker.Lock类,传timeout=5参数 - 注意:
portalocker锁的是文件内容,不是路径名——符号链接指向同一inode时仍能互斥
import portalocker
<p>with open("data.txt", "a+") as f:
try:
portalocker.lock(f, portalocker.LOCK_EX)
f.write("locked write\n")
f.flush()
finally:
portalocker.unlock(f) # 或直接让with块退出触发<strong>exit</strong></p>
为什么os.open+flock比open()+threading.Lock更可靠
threading.Lock只在单进程内有效,完全无法阻止其他Python进程或C程序同时写入。而文件锁是内核级的,所有语言、所有进程都受约束。
另一个常见误区是用os.rename做“原子替换”来规避锁——它只保证重命名操作本身原子,不保证写入过程不冲突。如果两个进程同时写临时文件再rename,仍可能覆盖。
- 真正需要独占访问时,必须在
open之后、首次读写之前完成加锁 - 避免在锁内做耗时操作(如网络请求、大文件读取),否则阻塞其他进程太久
- 锁文件和业务文件分离(例如锁
data.txt.lock)虽可行,但增加竞态风险:先检查锁文件存在再打开业务文件,中间可能被抢占
跨进程文件互斥没有银弹,flock和win32file.CreateFile是最接近“独占打开”语义的底层机制,其他方案都是妥协。选哪个取决于目标平台和是否接受第三方依赖。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











