之前在文章《深入理解 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 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 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
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
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:
的用法,参考官方文档。