之前在文章《深入理解 Python 中的类与元类》中讲了使用 __new__() 方法可以很方便地实现单例模式。但其实那段代码是线程不安全的。
验证代码如下:
import threading
import time
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
time.sleep(1)
cls._instance = object.__new__(cls)
return cls._instance
def create():
obj = Singleton()
print(obj)
threads = []
for i in range(10):
t = threading.Thread(target=create)
threads.append(t)
for t in threads:
t.start()
输出结果如下图:
正确的线程安全的实现方法是:
import logging, threading
class Singleton(object):
_instance = None
_lock = threading.Lock()
_initialized = False
def __new__(cls, *args, **kwargs):
if cls._instance is None:
with cls._lock:
if cls._instance is None:
cls._instance = object.__new__(cls)
return cls._instance
def __init__(self):
cls = self.__class__
if not cls._initialized:
with cls._lock:
if not cls._initialized:
cls._initialized = True
# 在此处进行成员变量的声明与初始化
self.logger = logging.getLogger(cls.__name__)
self.logger.debug('Init %s singleton', cls.__name__)
pass
为什么要嵌套两次判断 cls._instance is None 呢?
其实不加第一层判断也是可以的,但由于获取线程锁是一个“费时”的操作,所以加第一层判断其实就是为了避免每次调用 __new__() 方法都要去获取线程锁。
with lock: 的用法,参考官方文档。
