如何在Python中正确实现基于进程的锁?

2024-05-23 22:36:19 发布

您现在位置:Python中文网/ 问答频道 /正文

问题陈述:

我有一个从外部工具调用的脚本,它在一组文件中维护其实例的状态。我认为处理这个问题最实际的方法是简单地使用一个锁序列化脚本实例,该锁可以(a)在它尚未被锁定时,(b)在它被释放之后,(c)在持有它的进程消失之后。在

我还不确定是否有必要在现有进程崩溃或其他情况下立即唤醒下一个等待进程,因为这无论如何都是一种例外情况。但是下一个操作(可能由重启触发)必须能够成功运行。在

该脚本依赖于NetworkManager,而NetworkManager目前只在Linux上运行。因此,简单的解决方案比跨平台的解决方案更可取。另一方面,跨平台解决方案可能对大量stackoverflow访问者有用。在

进一步讨论:

在这个问题上,我发现问题的答案并不是很有用。尤其是关于处理过时锁的部分几乎没有得到解决。在

我希望继续使用上下文管理器API,并且只使用Linux安装中常见的库。我想在标准库和任何常见的安装中都没有完美的解决方案,所以我想我需要使用一些较低级别的API实现上下文管理器。在

当前代码使用lockfile模块,它似乎根本不关心过时的锁。除了文件系统之外,脚本实例不需要共享任何东西,因此基于模块的多处理解决方案在这里似乎不适用。我在考虑结合使用pidfilefcntl,还考虑了一个unix套接字,它可以用来等待其他脚本完成。我想知道为什么在Python中找不到一个基于上下文管理器的标准工具。在

有关脚本的实时版本(将随着新补丁的接受而更改):

http://www.nlnetlabs.nl/svn/dnssec-trigger/trunk/dnssec-trigger-script.in

源代码的相关部分:

def run(self):
    with lockfile.FileLock("/var/run/dnssec-trigger/dnssec-trigger"):
        log.debug("Running: {}".format(self.method.__name__))
        self.method()

Tags: 工具实例self脚本api管理器标准进程
2条回答

我在上游提交了以下实施方案:

class Lock:
    """Lock used to serialize the script"""

    path = "/var/run/dnssec-trigger/lock"

    def __init__(self):
        # We don't use os.makedirs(..., exist_ok=True) to ensure Python 2 compatibility
        dirname = os.path.dirname(self.path)
        if not os.path.exists(dirname):
            os.makedirs(dirname)
        self.lock = open(self.path, "w")

    def __enter__(self):
        fcntl.lockf(self.lock, fcntl.LOCK_EX)

    def __exit__(self, t, v, tb):
        fcntl.lockf(self.lock, fcntl.LOCK_UN)

它不是完全通用的(路径名是硬编码的),它没有关闭文件描述符,它可能会在其他方面得到改进,但我仍然想包括它作为参考。在

您可以使用contextlib创建自己的上下文管理器,并使用fcntl来发出锁定调用。请注意,这些可以设置为非阻塞。在

contextlib和{}都是标准库的一部分。在

如果要尝试使用过时的锁,可以尝试启动该进程两次,并向其中一个发出SIGKILL命令—您应该会看到另一个进程释放了锁。在

import fcntl
import contextlib

@contextlib.contextmanager
def lock(fname):
    with open(fname, "w") as f:
        print "Acquiring lock"
        fcntl.lockf(f, fcntl.LOCK_EX)
        print "Acquired lock"

        yield

        print "Releasing lock"
        fcntl.lockf(f, fcntl.LOCK_UN)
        print "Released lock"


if __name__ == "__main__":
    import os
    print "PID:", os.getpid()

    import time
    print "Starting"
    with lock("/tmp/lock-file"):
        time.sleep(100)
    print "Done"

相关问题 更多 >