前提:了解如何实现单例
了解完单例的实现模式后,我们接来看一段带线程锁的单例代码:
import threadingclass FormatResourceFactory:"""根据云平台创建对应云平台格式化类实例"""_instance_lock = threading.Lock()def __init__(self):self.cloud_dict = {CLOUD_TYPE: "_create_aliyun_instance",}def __new__(cls, *args, **kwargs):"""支持多线程的单例模式:param args::type args::param kwargs::type kwargs:"""if not hasattr(FormatResourceFactory, "_instance"):with FormatResourceFactory._instance_lock:if not hasattr(FormatResourceFactory, "_instance"):FormatResourceFactory._instance = object.__new__(cls)return FormatResourceFactory._instance
为什么要用到线程锁?
在多线程环境中,当多个线程同时尝试创建一个类的实例时,可能会出现竞态条件(race condition),导致创建多个实例,破坏了单例模式的目的。为了确保在多线程环境下也能正确实现单例模式,就需要使用线程锁来同步实例的创建过程。总的来说就是:
-
避免多次实例化: 当多个线程几乎同时到达
__new__
方法中检查实例是否存在时,如果没有适当的同步机制,可能会有多个线程通过这个检查,并分别创建实例,从而破坏了单例的唯一性。 -
保证状态一致性: 在多线程环境中,如果多个线程同时访问和修改共享资源(在这个例子中就是单例对象),那么就可能出现数据不一致的问题。通过加锁,可以确保在某个线程完成对象的创建之前,其他线程不会干扰这个过程。
如何工作?
在FormatResourceFactory
的__new__
方法中,我们看到使用了_instance_lock
来保护实例创建的过程。具体步骤如下:
- 首先检查是否有已有的实例:
if not hasattr(FormatResourceFactory, "_instance")
。 - 如果没有实例存在,则尝试获取锁:
with FormatResourceFactory._instance_lock:
。这个with
语句块确保了即使在创建过程中发生异常,锁也会被正确释放。 - 再次检查是否已经有其他线程创建了实例:
if not hasattr(FormatResourceFactory, "_instance")
。这是因为可能在获取锁的过程中,另一个线程已经创建了实例。 - 创建实例:
FormatResourceFactory._instance = object.__new__(cls)
。 - 返回实例。
示例代码
下面是带有线程锁的单例模式的简化示例:
import threadingclass Singleton:_instance_lock = threading.Lock()_instance = Nonedef __new__(cls, *args, **kwargs):if cls._instance is None:with cls._instance_lock:if cls._instance is None:cls._instance = super().__new__(cls)return cls._instance# 使用示例
singleton1 = Singleton()
singleton2 = Singleton()assert singleton1 is singleton2 # 应该返回True,表示两个变量引用的是同一个实例