Python多线程下单例模式实现

Python多线程下单例模式实现

单例模式算是在python用的比较多的设计模式,比较典型的应用场景包括:应用配置、日志文件、数据库调用…
其特点是在整个系统周期仅会初始化一次,因此可以减少内存占用空间以及避免多次实例化。比如类AppConfig会在整个系统多次进行调用,如果每次调用都进行实例化,无疑会拖慢系统运行效率。

关于单例模式实现, 网上有很多花里胡哨的方法,这里介绍一种比较常见的方法。使用__new__方法实现。

基本原理

python中实例化一个类时会在调用__init__方法前调用__new__方法,其接受的第一个参数是类本身,其余参数和__init__方法保持一致。因此可以在类中定义一个_instantce变量,在第一个进行示例化后将实例化的类保存到_instance中,之后实例化时只需判定_instance变量是否为空即可。

以实现一个全局计数器类为例

一个最简单的版本,这种实现方法会多次调用__init__函数,并且存在多线程重复实例化问题。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# -*- coding: utf-8 -*-

class GlobalCount(object):
# 用来保存实例化后的类实例
_instance = None

def __new__(cls, *args, **kwargs):
if cls._instance:
print("跳过实例化")
return cls._instance
else:
cls._instance = super(GlobalCount, cls).__new__(cls)
return cls._instance

def __init__(self):
self._count = 0

@property
def count(self):
return self._count

@count.setter
def count(self, value):
self._count = value

if __name__ == "__main__":
for i in range(10):
gc = GlobalCount()
gc.count = gc.count + 1
print(gc.count)

运行这段代码结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1
跳过实例化
1

可以看到,虽然避免了重复实例化的问题,但并没有达到我们想要的效果。

避免多次调用__init__函数

只需要在添加一个类变量:_init_flag作为标志位即可。在__init__方法内部将初始化相关的代码移动到if语句内,并在完成后将_init_flag设为False即可。

1
2
3
4
5
6
7
8
9
10
11
12
class GlobalCount(object):
# 用来保存实例化后的类实例
_instance = None
_init_flag = True
def __new__(cls, *args, **kwargs):
if cls._instance:
print("跳过实例化")
return cls._instance
else:
cls._instance = super(GlobalCount, cls).__new__(cls)
return cls._instance
...

保证多线程安全

继续完善GlobalCount类,目前在单线程下已经可以完美的工作了,但在多线程下有可能会存在多个线程同时进行实例化。可以使用threading包中的lock方法进行加锁处理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import threading

class GlobalCount(object):
# 用来保存实例化后的类实例
_instance = None
_init_flag = True
_instance_lock = threading.Lock()
def __new__(cls, *args, **kwargs):
with GlobalCount._instance_lock:
if cls._instance:
print("跳过实例化")
return cls._instance
else:
cls._instance = super(GlobalCount, cls).__new__(cls)
return cls._instance

总结

总结一下,为了实现GlobalCount类,我们用到了__new__方法、property语法糖以及threading

Python多线程下单例模式实现

https://www.huihuidehui.com/posts/d673f77c.html

作者

辉辉的辉

发布于

2022-08-22

更新于

2022-08-22

许可协议

评论