python3单例模式_Python3脚本单进程实例/单例模式实现

本文详细介绍了如何在Python中实现单进程实例和单例模式。通过示例代码展示了Linux平台使用fcntl和通用平台使用portalocker库创建单进程实例,确保同一时间只有一个进程运行。同时,解释了单例模式的概念,提供了单例模式的实现代码,证明无论实例化多少次,始终返回同一对象。最后,通过实验验证了单例模式在多进程中可以存在多个实例,明确了两者之间的区别。
摘要由CSDN通过智能技术生成

一、说明

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

一方面,shell中没有类和类实例的概念,所以一般说“单实例”都是指“单进程实例”,没有设计模式中“单例”的概念;另一方面,由于单进程实例和单例都是强调“唯一一份”所以在长时间里以为他们是相同的一个东西,和shell一样笼统地称为单实例就好了。

但现在看来他们不是一回事,“单进程实例”讨论的环境是整个内存、面向的对象是文件、结果是要么干掉原来的进程新启一个进程要么结束当前的进程保留原来的进程。

“单例模式”讨论的环境是一个进程内、面向的对象是类,结果是不管你在哪、调用多少次返回的都是同一个类实例。也就是说,如果是不同进程,那么是可以返回不同的类实例的(应该说就没法返回相同的类实例)。

二、单进程实例实现

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

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

importosimporttimeimportfcntlclassTest():#此函数用于获取锁

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)#将当前进程号写入文件

#如果获取不到锁上一步就已经异常了,所以不用担心覆盖

self.fd.writelines(str(os.getpid()))#写入的数据太少,默认会先被放在缓冲区,我们强制同步写入到文件

self.fd.flush()except:print(f"{file_name} have another instance running.")

exit(1)def __init__(self):

self._get_lock()defhello_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()

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

安装方法:pip install portalocker

importosimporttimeimportportalockerclassTest():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)#将当前进程号写入文件

#如果获取不到锁上一步就已经异常了,所以不用担心覆盖

self.fd.writelines(str(os.getpid()))#写入的数据太少,默认会先被放在缓冲区,我们强制同步写入到文件

self.fd.flush()except:print(f"{file_name} have another instance running.")

exit(1)def __init__(self):

self._get_lock()defhello_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()

三、单例模式实现

3.1 单例模式示例代码

importtimeimportthreadingimportdatetimeclassSingleton:

_instance_lock=threading.Lock()def __init__(self):pass

def __new__(cls, *args, **kwargs):if not hasattr(Singleton, "_instance"):

with Singleton._instance_lock:if not hasattr(Singleton, "_instance"):

Singleton._instance= super(Singleton, cls).__new__(cls, *args, **kwargs)returnSingleton._instancedefmain_logic(self):#打印自己及当前时间

print(f"instance--{self}\n"f"now time--{datetime.datetime.now().strftime('%H:%M:%S')}")

time.sleep(10)if __name__ == "__main__":

obj1=Singleton()

obj2=Singleton()

obj1.main_logic()

obj2.main_logic()

3.2 确认单例模式不管实例化多少次都返回同一个对象

运行代码,可以看到两个实例是一样的

3.3 确认单例模式可以有多个进程实例

我们在最开始说单进程实例和单例模式是不同层次的两个东西,不能相互代替。为了消除这个疑虑,尤其是单例模式可以代替单进程实例的疑虑,我们来做一下实验。

在相同时间段内,打开两个窗口分别运行代码,可以看到两次都成功了,即使用单例模式的代码在内存中是可以有多个进程实例的。

参考:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值