Python3脚本单进程实例实现

一、说明

一方面,前边写了“Linux shell脚本单实例模式实现”,python也是日常需要使用的,所以也想来看python中如何实现。

另一方面,shell中没有类和实例的概念,所以我以为“单进程实例”和设计模式中的“单例模式”是一个意思,但实际来看还是有些差别的。

“单进程实例”要求的是在整个内存中,一个文件只有一个进程实例。“单例模式”能保证的是类只有一个实例,一是说他可能被同进程其他代码在多处调用、实例化然而他总是返回那一个实例,二是即便只有他一个文件一个类我们仍可以多次运行而效果只是一个进程中只有该类的一个实例。

或者用协议的级别进行类比,“单进程实例”和”单例模式“他们所处的层级是不一样的,“单进程实例”是整个内存级,“单例模式”是整个进程级。

二、Linux平台实现--使用标准库fcntl

linux平台可以通过python标准库fcntl来实现锁

import os
import time
import fcntl

class Test():
    # 此函数用于获取锁
    def _get_lock(self):
        file_name = os.path.basename(__file__)
        # 为了统一按linux的习惯放到/var/run目录去
        lock_file_name = f"/var/run/{file_name}.pid"
        # 是读还是写还是什么模式并不重要,因为只是看能不能获取文件锁而不一定要写入内容
        # 但是这个一定要是成员变量self.fd而不能是局部变量fd
        # 因为实际发现当python发现局部变量fd不再使用时会将其回收,这就导致后边再运行时都能获取到锁
        self.fd = open(lock_file_name, "w")
        try:
            # #define LOCK_SH 1 /* Shared lock.  */   共享锁
            # #define LOCK_EX 2   /* Exclusive lock.  */ 互斥锁
            # #define LOCK_UN 8 /* Unlock.  */ 解锁
            # LOCK_NB--非阻塞模式。
            # 阻塞模式--获取不到锁时一直等待
            # 非阻塞模式--获取不到锁,直接抛出异常
            fcntl.flock(self.fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
        except:
            print(f"{file_name} have another instance running.")
            exit(1)

    def __init__(self):
        self._get_lock()

    def hello_world(self):
        print("hello world!")
        time.sleep(30)

    # 从观察到的现像看,占用锁的进程被关闭后,锁也就自动释放了
    # 也就是说,其实并不需要在最后自己主动释放锁
    def __del__(self):
        fcntl.flock(self.fd, fcntl.LOCK_UN)

if __name__ == "__main__":
    obj = Test()
    obj.hello_world()

三、通用平台实现--使用第三方库portalocker

安装方法:pip install portalocker

pypi地址:https://pypi.org/project/portalocker/

github地址:https://github.com/WoLpH/portalocker

import os
import time
import portalocker

class Test():
    def _get_lock(self):
        file_name = os.path.basename(__file__)
        # linux等平台依然使用标准的/var/run,其他nt等平台使用当前目录
        if os.name == "posix":
            lock_file_name = f"/var/run/{file_name}.pid"
        else:
            lock_file_name = f"{file_name}.pid"
        self.fd = open(lock_file_name, "w")
        try:
            portalocker.lock(self.fd, portalocker.LOCK_EX | portalocker.LOCK_NB)
        except:
            print(f"{file_name} have another instance running.")
            exit(1)

    def __init__(self):
        self._get_lock()

    def hello_world(self):
        print("hello world!")
        time.sleep(30)
    
    # 和fcntl有点区别,portalocker释放锁直接有unlock()方法
    # 还是一样,其实并不需要在最后自己主动释放锁
    def __del__(self):
        portalocker.unlock(self.fd)


if __name__ == "__main__":
    obj = Test()
    obj.hello_world()

参考:

https://stackoverflow.com/questions/28470246/python-lockf-and-flock-behaviour

https://www.v2ex.com/t/191267

https://cloud.tencent.com/developer/article/1115821

https://zhuanlan.zhihu.com/p/25134841

https://www.oreilly.com/library/view/python-cookbook/0596001673/ch04s25.html

https://stackoverflow.com/questions/1422368/fcntl-substitute-on-windows

猜你喜欢

转载自www.cnblogs.com/lsdb/p/12102418.html