如何安全地从多个脚本实例写入同一文件?

2 投票
2 回答
1413 浏览
提问于 2025-04-18 10:28

我有一个脚本,它会在监控目录中检测到新文件事件时被 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,其他的数据库也可能可以使用。如果另一个脚本正在处理共享数据,你可能会发现你的脚本会被暂时阻塞,但这其实是你想要的效果。

使用现有的服务

就像数据库一样,像Redisetcd这样的键值存储也可以是解决方案。

运行你自己的服务

写这样一个服务其实并不难(我建议使用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)

撰写回答