python多线程中只初始化一次的单例模式

python中的单例可以利用__new__和__init__来实现。每次创建实例的时候总会获得同一个实例,但是每次也会执行__init__方法。这就会造成单例中的属性会被修改,更重要的是实例会被重新初始化。有时候我们并不希望再次初始化实例,我们希望直接获得已经创建好的实例。应用类变量和锁机制,可以实现需求。

一个简单的单例

import threading
from concurrent.futures.thread import ThreadPoolExecutor


class Singleton:
    _instance = None

    def __new__(cls, *args, **kwargs):

        if cls._instance is None:
            cls._instance = super().__new__(cls)
        return cls._instance

    def __init__(self, num):

        self.num = num
        self.__init_flag = True

    def get_name(self):
        return self.num


def run_job(num):
    obj = Singleton(num)
    print("Thread:%s ID:%s NUM:%s" % (num, id(obj), obj.num))


with ThreadPoolExecutor(max_workers=5) as pool:
    for line in range(10):
        pool.submit(run_job, line)

上面样例的执行结果为。

Thread:0 ID:43341256 NUM:0
Thread:1 ID:43341256 NUM:1
Thread:2 ID:43341256 NUM:2
Thread:3 ID:43341256 NUM:3
Thread:4 ID:43341256 NUM:4
Thread:5 ID:43341256 NUM:5Thread:6 ID:43341256 NUM:6
Thread:7 ID:43341256 NUM:7
Thread:8 ID:43341256 NUM:8
Thread:9 ID:43341256 NUM:9

从结果中看到执行的10个线程中所创建的实例是同一个,因为实例的 id 是相同的,所以实例对象所指的内存地址是同一个,是同一个实例。但是它们的属性 num 是不同的,这是因为虽然实例是同一个,内存地址也是一个,但是每个实例都初始化了一遍(执行了一遍 __init__ 方法),初始化的时候对实例的属性 num 值进行了覆盖。

它们确确实实是同一个实例,但是属性值却发生了改变。这就像是“一个人”作为一个实例,而这个人改了名字,本来叫“小小”,后来改名叫“大大”,不管叫啥他都是同一个人。

只初始化一次的单例模式

有些时候我们创建一个单例后并不想让它修改属性。我们想第一次创建实例完成后,第二次创建的时候直接返回单例,不要在去执行初始化方法,避免多次初始化带来的资源和时间开销。样例代码如下所示。

import threading
from concurrent.futures.thread import ThreadPoolExecutor


class Singleton:
    _lock_1 = threading.Lock()
    _lock_2 = threading.Lock()
    _instance = None
    __init_flag = False

    def __new__(cls, *args, **kwargs):
        if cls._instance:
            return cls._instance
        with cls._lock_1:
            if cls._instance is None:
                cls._instance = super().__new__(cls)
            return cls._instance

    def __init__(self, num):

        if self.__init_flag:
            return

        with self._lock_2:
            if self.__init_flag:
                return

            self.num = num
            self.__init_flag = True

    def get_name(self):
        return self.num


def run_job(num):
    obj = Singleton(num)
    print("Thread:%s ID:%s NUM:%s" % (num, id(obj), obj.num))


with ThreadPoolExecutor(max_workers=10) as pool:
    for line in range(50):
        pool.submit(run_job, line)

# task = []
# for i in range(10):
#     t = threading.Thread(target=run_job, args=(i,))
#     task.append(t)
#
# for one in task:
#     one.start()
#
# for one in task:
#     one.join()

执行结果为。

Thread:0 ID:43669064 NUM:0
Thread:1 ID:43669064 NUM:0
Thread:2 ID:43669064 NUM:0
Thread:3 ID:43669064 NUM:0
Thread:4 ID:43669064 NUM:0
Thread:5 ID:43669064 NUM:0
Thread:6 ID:43669064 NUM:0
Thread:7 ID:43669064 NUM:0Thread:8 ID:43669064 NUM:0
Thread:9 ID:43669064 NUM:0

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值