繁体   English   中英

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

[英]How to properly implement a process based lock in Python?

问题陈述:

我有一个从外部工具调用的脚本,该脚本在一组文件中维护其实例的状态。 我认为最实用的处理方法是使用一个锁简单地序列化脚本实例,并具有以下能力:(a)尚未锁定时,(b)释放后以及(c) )持有程序后消失了。

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

该脚本取决于NetworkManager,后者目前仅在Linux上运行。 因此,一种简单的解决方案优于跨平台解决方案。 另一方面,跨平台解决方案对于大量的堆栈溢出访问者可能有用。

进一步讨论:

我在stackoverflow上找到了许多相关的问题和答案,但是(1)问题并不像这个问题那么具体,并且(2)答案似乎不适用于这种情况。 尤其是有关处理过时锁的部分几乎没有解决。

我想继续使用上下文管理器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()

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

contextlibfcntl都是标准库的一部分。

为了试验陈旧的锁,可以尝试两次启动该进程,然后向两个进程之一发出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"

我在上游提交了以下实现:

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)

它不是完全通用的(路径名是硬编码的),它不会关闭文件描述符,并且可能会以其他方式进行改进,但我仍然希望将其包括在内以供参考。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM