如何安全地从多个脚本实例写入同一文件?
我有一个脚本,它会在监控目录中检测到新文件事件时被 incrond 启动。在这个脚本里,我有一个函数用来读取和保存一些内容到配置文件中。目前,我尽量不让这个脚本的多个实例同时运行,以避免出现竞争条件,从而导致保存的文件损坏。
现在我在想,如何让这个函数在多个脚本实例尝试写入同一个文件时,安全地写入配置文件。我前几天读到关于使用 fcntl 模块进行文件锁定的内容,但我觉得有点难懂,不太确定它是怎么工作的。
def save_task_details(resp, filepath):
conf = ConfigParser.SafeConfigParser()
conf.read(CFG_PATH)
filehash = get_filehash(filepath)
if not conf.has_section(filehash):
conf.add_section(filehash)
conf.set(filehash, "filename", os.path.basename(filepath))
conf.set(filehash, "id", resp.id)
# ...
with open(CFG_PATH, "wb") as configfile:
conf.write(configfile)
2 个回答
0
从多个进程写入同一个文件总是比较棘手的,最好尽量避免这样做。
这里有几种选择:
文件锁定
没有标准库可以使用。可能会遇到锁过期的问题,导致文件无法访问。
写入文件的数据库
一个不错的选择是sqlite,其他的数据库也可能可以使用。如果另一个脚本正在处理共享数据,你可能会发现你的脚本会被暂时阻塞,但这其实是你想要的效果。
使用现有的服务
就像数据库一样,像Redis
或etcd
这样的键值存储也可以是解决方案。
运行你自己的服务
写这样一个服务其实并不难(我建议使用zmq
进行进程间通信,可以选择简单的方式(不依赖其他包)或者作为zerorpc
的一部分,这样可以很容易地暴露服务)。
1
文件锁定似乎有点麻烦,而且数据库不太适合用来写配置文件。所以,如果你不介意使用第三方模块,并且你是在 *nix 系统上工作,你可以考虑使用一个叫做 posix_ipc 的命名信号量来协调对配置文件的写入。这样只需要写很少的额外代码:
import posix_ipc as ipc
def save_task_details(resp, filepath):
# ...
with ipc.Semaphore('/config_semaphore', flags=ipc.O_CREAT, initial_value=1), open(CFG_PATH, "wb") as configfile:
conf.write(configfile)