[英]how to open (create if not exists) a file while acquiring exclusive lock avoiding races
在 python 2.7 中,是否有可能(以及如何)在单个原子(无竞争)操作中进行:
上下文:我有一个 Python 程序,可以根据 URL/md5 列表获取文件; 如果列表中的文件存在并且它的 md5 匹配,则会跳过它。 如果没有,它将被下载。 现在,该程序可能有多个实例处理可能重叠的不同列表。
这个问题几乎是我需要做的,但在我的情况下,我需要以任何一种方式锁定文件以检查它的 md5,同时防止其他人这样做。 另外,我不需要在操作之前知道文件是否存在; 如果它刚刚创建,该文件将为空,并且它的 md5 将不匹配,因此无论如何都会下载它。
我专门在 Linux 上使用这个程序,但欢迎跨平台解决方案。
编辑:最后我通过以下方式解决了我的问题:
就目前而言,单个原子步骤不支持所需的操作,但也不需要。
至少根据这份综合报告,这是不可能的:
mv -T <oldsymlink> <newsymlink>
原子改变所述目标<newsymlink>
到目录指向<oldsymlink>
和部署新代码时,是不可缺少的。 2010-01-06 更新:两个操作数都是符号链接。 (所以这不是系统调用,它仍然有用。)一位读者指出ln -Tfs <directory> <symlink>
完成相同的事情,而无需第二个符号链接。 添加 2010-01-06。 2010 年 1 月 6 日删除:strace(1) 显示ln -Tfs <directory> <symlink>
实际上ln -Tfs <directory> <symlink>
调用了 symlink(2)、unlink(2) 和 symlink(2),从而取消了它从此页面的资格。mv -T <oldsymlink> <newsymlink>
最终调用 rename(2) ,它可以原子地替换<newsymlink>
。 警告 2013-01-07:这不适用于 Mac OS X,其 mv(1) 不调用 rename(2)。 MV(1)。link(oldpath, newpath)
创建一个名为 newpath 的新硬链接,指向与 oldpath 相同的 inode 并将链接计数增加 1。 如果 newpath 已经存在,这将失败并显示错误代码 EEXIST,这使其成为在线程或进程之间锁定文件的有用机制,这些线程或进程都可以就名称 newpath 达成一致。 我更喜欢这种用于整个文件锁定的技术,因为该锁定对 ls(1) 是可见的。 链接(2)。symlink(oldpath, newpath)
操作与 link(2) 非常相似,但在新的 inode 处创建符号链接,而不是到同一 inode 的硬链接。 符号链接可以指向目录,而硬链接不能,这使得它们在锁定整个目录时与 link(2) 完美类似。 如果 newpath 已经存在,这将失败并显示错误代码 EEXIST,这使其成为对目录也适用的 link(2) 的完美类比。 注意目标 inode 已被删除的符号链接(“悬空”符号链接)——open(2) 将失败并显示错误代码 ENOENT。 应该提到的是,inode 是一种有限资源(这台特定的机器有 1,245,184 个 inode)。 符号链接(2)。 添加 2010-01-07rename(oldpath, newpath)
可以自动更改路径名,前提是 oldpath 和 newpath 在同一个文件系统上。 如果 oldpath 不存在,这将失败并显示错误代码 ENOENT,启用进程间锁定,就像上面的 link(oldpath, newpath) 一样。 当有问题的文件稍后被取消链接时,我发现这种技术更自然。 重命名(2)。open(pathname, O_CREAT | O_EXCL, 0644)
创建并打开一个新文件。 (不要忘记在第三个参数中设置模式!)如果路径名存在,O_EXCL 指示此操作失败并返回错误代码 EEXIST。 这是决定哪个进程应该处理任务的有用方法:成功创建文件的人。 打开(2)。mkdir(dirname, 0755)
创建一个新目录,但如果 dirname 存在,则会失败并显示错误代码 EEXIST。 这为目录提供了与 O_EXCL 为文件提供的相同机制 link(2) open(2)。 mkdir(2)。 添加 2010-01-06; 编辑 2013-01-07。
如您所见, open()
只能以原子方式用于创建新文件,而不能打开现有文件进行读取。 但是,如果您想使用这种方法,您可能需要使用 Python 的os.open()
,它是此系统调用的代理(不要与内置的open()
混淆)。
您也可以考虑使用数据库来完成这项任务,因为它们应该提供更高的可靠性(例如,如果您的文件托管在 NFS 上,它根本没有实现锁定并且 IIRC 是唯一的原子操作mkdir()
?)。
不,它不可能作为 Linux/UNIX 支持的基本操作。
您引用的答案中的 O_CREAT|O_EXCL 技术可以在这里工作。 您不是专门创建目标文件,而是专门创建一个锁文件,其名称可预测地从目标文件派生而来。 例如, os.path.join("/tmp", hashlib.md5(target_filename).hexdigest() + ".lock")
。
但是,正如其他人所建议的那样,您不清楚是否需要保护目标文件创建及其校验和 + 可能的替换。 fcntl
咨询锁将满足您的需求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.