Python: 锁定文件

3

我有一个运行在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上阻塞而不是抛出异常。


第二个进程能成功锁定该文件吗? - Winston Ewert
据我所知,是的,因为它从该调用返回并读取文件。 - Ambrosio
从这个函数返回的文件句柄你会怎么处理? - Winston Ewert
@Winston Ewert。谢谢您提出那个问题。我刚刚仔细检查了代码并发现文件句柄在处理完成之前被关闭了。现在似乎已经可以正常工作,除了第二个进程会在portalocker.lock上阻塞而不是抛出异常。 - Ambrosio
当我运行我的简单测试脚本时,我会收到一个异常。 - Winston Ewert
5个回答

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)

6
您正在使用LOCK_NB标志,这意味着调用是非阻塞的,并且在失败时将立即返回。这可能发生在第二个进程中。它仍然能够读取文件的原因是portalocker最终使用flock(2)锁定,并且正如flock(2)手册中所提到的那样:
“flock(2)仅放置咨询锁定;给予文件适当的权限,进程可以忽略flock(2)的使用并对文件执行I/O。”
要修复它,您可以直接使用fcntl.flock函数(在Linux上,portalocker只是它的一个薄包装),并检查返回的值以查看锁定是否成功。

3
不要使用 cron。Linux有inotify,可以在文件系统事件发生时通知应用程序。有一个名为pyinotify的Python绑定可用于inotify
因此,您不需要锁定文件--您只需要对IN_CLOSE_WRITE事件作出反应(即当打开用于写入的文件被关闭时)。 (您也不需要每分钟生成新进程。)
使用pyinotify的替代方法是incron,它允许您编写一个incrontab(非常类似于crontab),以与inotify系统交互。

我猜这个问题已经在评论中得到解决了,但这也是有用的建议。 - Jochen Ritzel
我需要运行一个守护进程来响应这些通知吗? - Ambrosio
@Ambrosio:是的,如果您使用pyinotify,则Python应用程序必须在运行,如果您使用incron,则incrond守护进程必须在运行。不过,我认为这比每分钟使用cron+ 1个进程更少占用资源。 - unutbu

1

手动创建一个旧式的.lock文件,放在你想要锁定的文件旁边,怎么样?

只需检查它是否存在;如果不存在,则创建它;如果存在,则提前退出。完成后,删除它。


这个答案是正确的(做得很好)。创建一个只读文件(检查是隐式的),如果它已经存在,它将返回一个错误。 - darkfeline
5
许多程序都使用这种技术,例如Skype和Truecrypt。它们也都有一个共同的缺点,即如果它们崩溃或意外关闭,则最终用户必须找到并删除所有的.lock文件。这绝对是错误的做法,因此请使用适当的锁定库。 - Score_Under
1
如果正确实现,这个答案是正确的。大多数使用此功能的程序缺少的是锁定文件中的随机字符串。创建锁定文件时,请将随机字符串放入其中。然后读取该字符串 - 如果相同,则已锁定。如果不是,请等待锁定文件不存在,然后重试。 - Wayne Workman

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接