单例模式
近日学习了设计模式相关数据,根据自身使用语言的特性,实现设计模式的案例。
概念
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
(一) 线程不安全的单例模式
由于python没有private、public等写法,所以要实现单例模式,需要重构类的__new__
方法。
在Python中,__init__
方法常用创建类对象的初始化。__new__
方法创建对象实例。所以通过修改__new__
方法,不让其调用__init__
方法即可。如下:
#-*- coding:utf-8 -*-
# 文件名 : single_instance.py
# 作者 : WangYi
__author__ = 'WangYi'
import threading
class SingleServer(object):
_instance = None
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not cls._instance:
if not cls._instance:
cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
return cls._instance
def run(n):
print("Treading_%d SingleServer id:%d" % (n, id(SingleServer())))
def main():
t1 = threading.Thread(target=run, args=(1,))
t2 = threading.Thread(target=run, args=(2,))
t3 = threading.Thread(target=run, args=(3,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("End")
if __name__ == "__main__":
main()
结果如下:
Treading_1 SingleServer id:140054791900112
Treading_2 SingleServer id:140054791900112
Treading_3 SingleServer id:140054791900112
End
(二)线程安全的单例模式
在多线程的模式下,有可能A向内存申请创建实例A’,与其同时B也这在向内存中创建B’。因为两者同时进行完成创建实例条件,则最终两者获得了两个不同的实例。导致后续的错误。
时序图如下:
Created with Raphaël 2.1.0 A A 内存 内存 B B 申请实例对象 申请实例对象 与A同时申请 产生实例对象B’ 产生实例对象A’
由于CPython的GIL锁的原因,Python线程并未能够真正的发挥线程优势,尤其是在多核情况下,CPU也只能达到单核100%。
可以使用threading自带的Lock进行线程锁,保证资源的申请是安全的。修正的地方如下
import threading
Lock = threading.Lock()
class SingleServer(object):
_instance = None
def __init__(self):
pass
def __new__(cls, *args, **kwargs):
if not cls._instance:
try:
Lock.acquire() # double lock
if not cls._instance:
cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
finally:
Lock.release()
return cls._instance
def run(n):
print("Treading_%d SingleServer id:%d" % (n, id(SingleServer())))
def main():
t1 = threading.Thread(target=run, args=(1,))
t2 = threading.Thread(target=run, args=(2,))
t3 = threading.Thread(target=run, args=(3,))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("End")
if __name__ == "__main__":
main()
带参数的单例模式
发现提供的案例不够满足大家的需求,又添加了新的案例供大家参考。
import threading
Lock = threading.Lock()
class SingleServer(object):
_instance = None
def __init__(self, name, sex):
self.name = name
self.sex = sex
def __new__(cls, *args, **kwargs):
if not cls._instance:
try:
Lock.acquire() # double lock
if not cls._instance:
cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
print("create instance")
finally:
Lock.release()
return cls._instance
def printinfo(self):
""" 打印参数 """
print("My name is %s" % self.name)
print("I'm a %s" % self.sex)
def run(n, name, sex):
single = SingleServer(name, sex)
print("Treading_%d SingleServer id:%d" % (n, id(single)))
single.printinfo()
def main():
t1 = threading.Thread(target=run, args=(1, 'Test1', 'Male'))
t2 = threading.Thread(target=run, args=(2, 'Test2', 'Female'))
t3 = threading.Thread(target=run, args=(3, 'Test3', 'Male'))
t1.start()
t2.start()
t3.start()
t1.join()
t2.join()
t3.join()
print("End")
if __name__ == "__main__":
main()
结果表明:
create instance
Treading_1 SingleServer id:139799877954320
My name is Test1
I'm a Male
Treading_2 SingleServer id:139799877954320
My name is Test2
I'm a Female
Treading_3 SingleServer id:139799877954320
My name is Test3
I'm a Male
End
采用单例模式下,给SingleServer传递的参数以最后一次传递的参数为准。即__init__
函数依然有效。可提供用户进行初始化。