所有实例对象封装的数据都相同的场景。这样场景下, 应用单例模式可以减少重复创建对象, 从而节约内存。
在数据库的连接过程中, 每次连接过程中所用到的IP, 端口, 用户名 密码等相同。 由于每次单独连接数据库耗时比较长, 通常会先创建一个连接池, 该连接池已经与数据库创建好连接, 其它用户想连接到这个数据库先访问连接池, 由连接池分配连接,从而节约时间。
伪代码
1 class ConnectionPool:
2 __instance = None
3 def __init__(self):
4 self.addr = '127.0.0.1'
5 self.port = 8008
6 self.name = 'sql'
7 self.conn_list = [1,2,3,4,5,6,7,8,9,10]
8 def get_connection(self):
9 r = random.randrange(1, 11)
10 return r
11 P = ConnectionPool()
12 P.get_connection()
每次由客户端连接的时候, 都会创建新的对象。在这种重复创建对象, 且对象中封装的数据相同时候, 即可利用单例模式, 节约内存提升性能
单例模式伪代码:
class ConnectionPool:
2 __instance = None
3 def __init__(self):
4 self.addr = '127.0.0.1'
5 self.port = 8008
6 self.name = 'sql'
7 self.conn_list = [1,2,3,4,5,6,7,8,9,10]
8 @staticmethod
9 def get_instance():
10 if ConnectionPool.__instance:
11 return ConnectionPool.__instance
12 else:
13 ConnectionPool.__instance == ConnectionPool()
14 return ConnectionPool.__instance
15 def get_connection(self):
16 r = random.randrange(1, 11)
17 return r
通过一个私有变量,一个静态方法 get_instance()实现了一次创建对象,多次连接。
1. 单例只有一个实例
2. 静态方法、静态字段
3. 所有封装数据都一样时用单例模式
Python 中其他的使用场景
什么情况下才会使用单例模式:
所用使用单例模式的前提是我们的确用一个实例就可以搞定要解决的问题, 而不需要多个实例, 如果每个实例都要维护自己的实例, 这种情况下单例模式不适合。
最简单的实例代码
class Singleton(object):
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls)
return cls.__instance
s1 = Singleton()
s2 = Singleton()
print(s1)
print(s2)
结果
<__main__.Singleton object at 0x0000027060C48A20>
<__main__.Singleton object at 0x0000027060C48A20>
可以看出返回的结果都是同一个对象, 下面是一个 接近一个真实场景
class Singleton(object):
__instance = None
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls)
return cls.__instance
def __init__(self, number):
self.number = number
s1 = Singleton(2)
s2 = Singleton(9)
print(s1)
print(s2)
print(s1.number)
print(s2.number)
打印结果
<__main__.Singleton object at 0x000001FA2CB60668>
<__main__.Singleton object at 0x000001FA2CB60668>
9
9
可以看到是同一个对象实例, 打出的数据都是9 后面修改的也影像前面的值, 共享实例的变量
有共享数据就要想到并发访问的时候 数据问题。 当有多个线程同时去初始化对象的时候, 有可能就会同时判断 __instance is None, 从而进入初始化instance 的代码中。 为了解决这个问题必须通过同步锁来完成。
import threading
try:
from synchronize import make_synchronized
except ImportError:
def make_synchronized(func):
import threading
func.__lock__ = threading.Lock()
def synced_func(*args, **kws):
with func.__lock__:
return func(*args, **kws)
return synced_func
class Singleton(object):
instance = None
@make_synchronized
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = object.__new__(cls, *args, **kwargs)
return cls.instance
def __init__(self):
self.blog = "xiaorui.cc"
def go(self):
pass
def worker():
e = Singleton()
print id(e)
e.go()
def test():
e1 = Singleton()
e2 = Singleton()
e1.blog = 123
print e1.blog
print e2.blog
print id(e1)
print id(e2)
if __name__ == "__main__":
test()
task = []
for one in range(30):
t = threading.Thread(target=worker)
task.append(t)
for one in task:
one.start()
for one in task:
one.join()
以上伪代码没有进行验证, 但是感觉太复杂啦。
在同一时刻保证只有一个线程访问
保证单例模式的安全
import threading
def synchronized(func):
func.__lock__ = threading.Lock()
def lock_func(*args, **kwargs):
with func.__lock__:
return func(*args, **kwargs)
return lock_func
class Singleton(object):
__instance = None
@synchronized
def __new__(cls, *args, **kwargs):
if cls.__instance is None:
cls.__instance = super(Singleton, cls).__new__(cls) # 每一次实例化的时候都会返回这同一个instance 对象
return cls.__instance
def __init__(self, num):
self.num = num
a1 = Singleton(2)
a2 = Singleton(9)
print(a1)
print(a2)
print(a1.num)
print(a2.num)
这里使用装饰器的方式
在tornado 中 只有一个线程,如果是单例模式的话不管你并发多少 单例实例的对象都是同一个, 这个测试过的。