建议50:单例模式---多线程环境
修改__new__:
class S(object):
lock = threading.Lock()
def __new__(cls, *args, **kw):
if hasattr(cls, '_instance'):
print '1'
return cls._instance
cls.lock.acquire()
try:
cls._instance =object.__new__(cls, *args, **kw)
finally:
cls.lock.release()
print '3'
return cls._instance
def p(self):
print 'called p'
for i in range(5): s=S() print id(s) threading.Thread(target=s.p).start()
3 139857531623824 called p 1 139857531623824 called p 1 139857531623824 1 called p 139857531623824 1 called p 139857531623824 called p
装饰器:
from functools import wraps def singleton(cls): @wraps(cls) def getinstance(*args, **kw): if not hasattr(cls, '_instance'): cls._instance = cls(*args, **kw) return cls._instance return getinstance @singleton class MyClass(object): def p(self): print 'p called\n' import threading for i in range(5): m=MyClass() print id(m) threading.Thread(target=m.p).start()
139648094936592 p called 139648094936592 p called 139648094936592 p called 139648094936592 p called 139648094936592 p called
可以使用hasattr(cls, '_instance')判断,cls是否有_instance属性,决定是否构造实例。
也可以定义类变量_instance,判断cls的类变量_instance是否为None,决定是否构造实例。
也可以定义类变量objs为字典,判断cls的类变量objs是否有_instance的key,决定是否构造实例。
import threading class Singleton(object): _instance = None lock = threading.Lock() def __new__(cls, *args, **kw): if cls._instance is None: print '1' cls.lock.acquire() try: cls._instance =object.__new__(cls, *args, **kw) finally: cls.lock.release() print '3' return cls._instance def p(self): print 'called p' for i in range(5): s=Singleton() print id(s) threading.Thread(target=s.p).start()
import threading class Singleton(object): objs = {} lock = threading.Lock() def __new__(cls, *args, **kw): if cls not in cls.objs: print '1' cls.lock.acquire() try: cls.objs[cls] = super(Singleton, cls).__new__(cls, *args, **kw) finally: cls.lock.release() print '3' return cls.objs[cls] def p(self): print 'called p' for i in range(5): s=Singleton() print id(s) threading.Thread(target=s.p).start()
1 139630141873296 called p3
3 139630141873296 called p3 139630141873296 called p 3 139630141873296 called p 3 139630141873296 called p
修改元类:
class Singleton(type): def __call__(cls, *args, **kwargs): if not hasattr(cls, '_instances'): cls._instances = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances # Python2 class MyClass(object): __metaclass__ = Singleton def p(self): print 'p called' # Python3 # class MyClass(metaclass=Singleton): # def p(self): # print 'p called' for i in range(5): s=MyClass() print id(s) threading.Thread(target=s.p).start()
139924625706512 p called 139924625706512 p called 139924625706512 p called 139924625706512 p called 139924625706512 p called
最优雅的单例模式,多线程同样适用:
在Python的官方网站:给了两个例子是用装饰符来修饰类,从而使得类变成了单例模式,使得我们可以通过更加简单的方式去实现单例模式
例子:(这里只给出一个例子,因为更简单,另外一个大家可以看官网Singleton
def singleton(cls): instance = cls() instance.__call__ = lambda: instance return instance # # Sample use # @singleton class Highlander: x = 100 # Of course you can have any attributes or methods you like. print Highlander() is Highlander() is Highlander #=> True id(Highlander()) == id(Highlander) #=> True Highlander().x == Highlander.x == 100 #=> True Highlander.x = 50 Highlander().x == Highlander.x == 50 #=> True
这里简单解释下:
- 在定义class Highlander的时候已经执行完所有singleton装饰器中的代码,得到了一个instance,所以这之后所有对Highlander的调用实际上是在调用instance的_call_ 方法。
- 我们通过lambda函数定义了_call_方法让它始终返回instance,因此Highlander()和Highlander都返回instance
- 同时由于在类定义代码执行时就已经创建了instance,所以后续不论是多线程还是单线程,在调用Highlander时都是在调用instance的_call_方法,也就无需同步了。
- 最后我想说的是这种方法简直碉堡了~~~
附上我用于多线程的测试代码:
def singleton(cls): instance = cls() instance.__call__ = lambda: instance return instance @singleton class MyClass(object): def p(self): print '\np called\n' import threading for i in range(5): s = MyClass print id(s) threading.Thread(target=s.p).start()
import threading def singleton(cls): instance = cls() instance.__call__ = lambda: instance return instance @singleton class Highlander: x = 100 # Of course you can have any attributes or methods you like. def worker(): hl = Highlander() hl.x += 1 print hl print hl.x def main(): threads = [] for _ in xrange(50): t = threading.Thread(target=worker) threads.append(t) for t in threads: t.start() for t in threads: t.join() if __name__ == '__main__': main()
142 <__main__.Highlander instance at 0x7fafd0d51248><__main__.Highlander instance at 0x7fafd0d51248> 145 <__main__.Highlander instance at 0x7fafd0d51248> 145 <__main__.Highlander instance at 0x7fafd0d51248> 146 <__main__.Highlander instance at 0x7fafd0d51248>147
这里的代码有一点小问题,就是在打印的时候有可能x属性已经被别的线程+1了,所以有可能导致同一个数打印
posted on 2018-02-12 15:57 myworldworld 阅读(143) 评论(0) 收藏 举报