如果实现对同一个文件的读写,在各个进程之间互斥?
背景
默认情况下如果我们使用 open 函数来打开一个文件,另一个进程还是可以用 open 打开同一个文件。如果两个进程都向文件中写入数据的话,两个进程的数据相互覆盖(后面写的进程覆盖前面进程的数据)。这个结果通常情况下不是我们想看到的。
为了实现进程独占式的访问文件,我们需要比 open 更加低层的 API 才能实现。
解决方案
解决这个问题我们要用到两个标准库中的模块 os 和 fcntl ;其中 os 中定义了比 open 更加低层的文件访问 API `os.open`,fcntl 实现在整个操作系统层面的访问控制。只要我们程序都使用这个两个模块来访问文件,我们就能实现进程间的互斥访问。
假设我们现在要往 /tmp/a.log 文件中写入 `hello world\n`,要做到互斥访问代码可以这样写。
#!/usr/bin/env python3
import os
import time
import fcntl
import logging
logging.basicConfig(level=logging.INFO)
def exclusive_write(file:str="/tmp/a.log", content:str="hello world"):
"""
实现对给定文件的排他写入
Parameter
---------
file: str
目标文件的全路径
content:str
要写入内容
"""
# 第一步 打开文件
file_desc = os.open(file,os.O_CREAT | os.O_RDWR)
try:
# 第二步 加排他锁
fcntl.lockf(file_desc,fcntl.LOCK_EX | fcntl.LOCK_NB)
# 第三步 业务逻辑
os.truncate(file_desc,0)
os.write(file_desc,content.encode('utf8'));
# 第四步 sleep 一下方便测试
time.sleep(11)
except IOError as err:
logging.exception(err)
finally:
os.close(file_desc)
logging.info(f"close file {file_desc}")
if __name__ == "__main__":
exclusive_write("/tmp/a.log","hello world\n")
当第一个运行中的进程还没有退出的时候,我们再一次运行同一个程序,这个时候会报如下的错误。
python3 main.py
ERROR:root:[Errno 35] Resource temporarily unavailable
Traceback (most recent call last):
File "/private/tmp/jss/main.py", line 27, in exclusive_write
fcntl.lockf(file_desc,fcntl.LOCK_EX | fcntl.LOCK_NB)
BlockingIOError: [Errno 35] Resource temporarily unavailable
总结
可以看到由于加锁阶段的存在,当第二个进程申请锁的时候就直接报 BlockingIOError ,这样就实现了互斥的访问。