Why is this singleton implementation “not thread safe”?
1。@singleton装饰师
我找到了一种优雅的方法来装饰一个Python类,使它成为一个
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 31 32 33 34 35 36 37 | class Singleton: """ A non-thread-safe helper class to ease implementing singletons. This should be used as a decorator -- not a metaclass -- to the class that should be a singleton. The decorated class can define one `__init__` function that takes only the `self` argument. Also, the decorated class cannot be inherited from. Other than that, there are no restrictions that apply to the decorated class. To get the singleton instance, use the `Instance` method. Trying to use `__call__` will result in a `TypeError` being raised. """ def __init__(self, decorated): self._decorated = decorated def Instance(self): """ Returns the singleton instance. Upon its first call, it creates a new instance of the decorated class and calls its `__init__` method. On all subsequent calls, the already created instance is returned. """ try: return self._instance except AttributeError: self._instance = self._decorated() return self._instance def __call__(self): raise TypeError('Singletons must be accessed through `Instance()`.') def __instancecheck__(self, inst): return isinstance(inst, self._decorated) |
我在这里找到了代码:有没有一个简单,优雅的方式来定义单件?
上面的评论说:
[This is] a non-thread-safe helper class to ease implementing singletons.
不幸的是,我没有足够的多线程经验来看到"线程不安全"自己。
nbsp;
2。问题我在多线程的python应用程序中使用这个
有没有一种方法可以使这个代码完全线程安全?
如果前面的问题没有解决方案(或者解决方案过于繁琐),我应该采取什么预防措施来保持安全?
@阿兰·费伊指出,装修工的代码写得很糟糕。当然,任何改进都非常感谢。
在此,我提供我当前的系统设置:&Python3.6.3&64位Windows 10
我建议您选择一个更好的单例实现。基于元类的实现是最常用的。
至于线程安全,您的方法和上面链接中建议的任何方法都不是线程安全的:线程总是可能读取不存在的实例并开始创建一个实例,但另一个线程在存储第一个实例之前也会这样做。
您可以使用这个答案中建议的装饰器来保护基于元类的带有锁的单例类的
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 | import functools import threading lock = threading.Lock() def synchronized(lock): """ Synchronization decorator""" def wrapper(f): @functools.wraps(f) def inner_wrapper(*args, **kw): with lock: return f(*args, **kw) return inner_wrapper return wrapper class Singleton(type): _instances = {} @synchronized(lock) def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs) return cls._instances[cls] class SingletonClass(metaclass=Singleton): pass |
如果您关心性能,可以使用检查锁定检查模式来最小化锁定获取,从而改进已接受答案的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class SingletonOptmized(type): _instances = {} def __call__(cls, *args, **kwargs): if cls not in cls._instances: cls._locked_call(*args, **kwargs) return cls._instances[cls] @synchronized(lock) def _locked_call(cls, *args, **kwargs): if cls not in cls._instances: cls._instances[cls] = super(SingletonOptmized, cls).__call__(*args, **kwargs) class SingletonClassOptmized(metaclass=SingletonOptmized): pass |
区别在于:
1 2 3 4 5 | In [9]: %timeit SingletonClass() 488 ns ± 4.67 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) In [10]: %timeit SingletonClassOptmized() 204 ns ± 4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each) |