簡體   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