Python: 锁定文件

3 投票
5 回答
20936 浏览
提问于 2025-04-16 10:54

我有一个在Linux上运行的Python应用,每分钟通过cron(定时任务)调用一次。它会检查一个文件夹里的文件,如果找到文件,就会处理它,这个过程可能需要几分钟。我不想让下一个cron任务去处理正在被处理的文件,所以我用下面的代码锁定文件,这段代码调用了portalocker库。但是,问题是这似乎没有效果,下一个cron任务还是能获取到正在处理的文件的句柄。

def open_and_lock(full_filename):
    file_handle = open(full_filename, 'r')
    try:
        portalocker.lock(file_handle, portalocker.LOCK_EX
                            | portalocker.LOCK_NB)
        return file_handle
    except IOError:
        sys.exit(-1)

有没有什么办法可以锁定文件,让其他进程无法访问它呢?

更新

感谢@Winston Ewert的帮助,我检查了代码,发现文件句柄在处理完成之前就被关闭了。现在似乎可以正常工作了,不过第二个进程在portalocker.lock上会被阻塞,而不是抛出异常。

5 个回答

3

不要用 cron 来做这个。Linux 有个叫 inotify 的东西,它可以在文件系统发生变化时通知应用程序。还有一个专门为 inotify 提供的 Python 库,叫 pyinotify

所以,你不需要去锁定文件——你只需要对 IN_CLOSE_WRITE 事件做出反应(也就是当一个正在写的文件被关闭时)。这样你也不需要每分钟启动一个新进程。

另外一个可以替代 pyinotify 的选择是 incron,它允许你写一个 incrontab(和 crontab 的风格很像),来与 inotify 系统进行交互。

6

你正在使用LOCK_NB这个标志,这意味着这个调用是非阻塞的,也就是说如果失败了,它会立刻返回。这个情况可能发生在第二个进程中。之所以它仍然能读取文件,是因为portalocker最终使用的是flock(2)锁,正如在flock(2)手册中提到的:

flock(2)只放置建议性锁;只要对文件有合适的权限,进程可以选择忽略flock(2)的使用,直接对文件进行输入输出操作。

要解决这个问题,你可以直接使用fcntl.flock这个函数(portalocker其实就是在Linux上对它的一个简单封装),并检查返回的值来确认锁是否成功。

9

经过尝试了很多方法,这个方案在我的情况下有效。我有一个脚本可能会被同时执行多次。我需要这些脚本等着轮流去读写一些文件。锁文件不需要被删除,这样可以避免如果某个脚本在删除之前失败,就会阻止所有的访问。

import fcntl

def acquireLock():
    ''' acquire exclusive lock file access '''
    locked_file_descriptor = open('lockfile.LOCK', 'w+')
    fcntl.lockf(locked_file_descriptor, fcntl.LOCK_EX)
    return locked_file_descriptor

def releaseLock(locked_file_descriptor):
    ''' release exclusive lock file access '''
    locked_file_descriptor.close()

lock_fd = acquireLock()

# ... do stuff with exclusive access to your file(s)

releaseLock(lock_fd)

撰写回答