单例模式

  • 目的:确保某一个类只有一个实例存在,并且提供一个访问该实例的全局访问点

  • 单例模式只生成一个实例对象,减少了对系统资源的开销。当一个对象的产生需要比较多的资源,如读取配置文件、产生其他依赖对象时,可用产生一个"单例对象",然后永久驻留内存中,从而极大的降低开销。

  • 产生原因:在面向对象的程序设计中,当业务并发量非常大时,那么就会出现重复创建相同的对象,每创建一个对象就会开辟一块内存空间,而这些对象其实是一模一样的,那么有没有办法使用得内存对象只创建一次,然后再随处使用呢?单例模式就是为了解决这个问题而产生的。

方法一:使用模块

Python的模块是天然的单例模式,因为模块在第一次导入时,会生成.pyc文件,当第二次导入时,会直接加载.pyc文件,而不会再次执行模块代码。因此只需要把相关的函数和数据定义在一个模块中,就可以获得一个单例对象了。
单例类:

#!/bin/python

class Singleton(object):
    def foo(self):
        pass

singleton = Singleton()

使用:

from mysingleton import singleton
print singleton.foo()

方法二:使用装饰器

def Singleton(cls):
    _instance = {}
    def _singleton(*args, **kargs):
        if cls not in _instance:
            _instance[cls] = cls(*args, **kargs)
        return _instance[cls]
    return _singleton

@Singleton
class A(object):
    a = 1
    def __init__(self, x=0):
        self.x = x

a1 = A(2)
a2 = A(3)

方法三:使用类

class Singleton(object):
    def __init__(self):
        #time.sleep(1)
        pass
    
    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance 


import threading

def task(arg):
    obj = Singleton.instance()
    print (obj)

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

运行结果:

<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>
<__main__.Singleton object at 0x7f12386fab90>

看似没有问题,但如果加上sleep就会发现问题:

<__main__.Singleton object at 0x7fd22612fb50>
<__main__.Singleton object at 0x7fd22612fc90>
<__main__.Singleton object at 0x7fd22612fed0>
<__main__.Singleton object at 0x7fd22613f190>
<__main__.Singleton object at 0x7fd22613f410>
<__main__.Singleton object at 0x7fd22613f690>
<__main__.Singleton object at 0x7fd22613f910>
<__main__.Singleton object at 0x7fd22613fb90>
<__main__.Singleton object at 0x7fd22613fe10>
<__main__.Singleton object at 0x7fd2261470d0>

第一次没有问题是因为执行速度过快。
因此,这种方式不支持多线程。

解决办法:加锁
即:为加锁部分并发执行,加锁部分串行执行,速度降低,但保证了数据安全。

import threading
import time
class Singleton(object):
    _instance_lock = threading.Lock()
    def __init__(self):
        time.sleep(1)
        pass
    
    @classmethod
    def instance(cls, *args, **kwargs):
        with Singleton._instance_lock:
            if not hasattr(Singleton, "_instance"):
                Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance 



def task(arg):
    obj = Singleton.instance()
    print (obj)

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

time.sleep(20)
obj = Singleton.instance()
print obj

执行结果:

<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>
<__main__.Singleton object at 0x7fcb4ad30b10>

但还是有不足:
就是当程序执行时,执行了time.sleep(20)后,下面实例化对象时,此时已经是单例模式了,但我们还是加了锁,这样不太好,再进行一些优化,把intance方法,改成下面的这样就行,如下即为一个完整的支持多线程的单例类。

import time
import threading
class Singleton(object):
    _instance_lock = threading.Lock()

    def __init__(self):
        time.sleep(1)

    @classmethod
    def instance(cls, *args, **kwargs):
        if not hasattr(Singleton, "_instance"):
            with Singleton._instance_lock:
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = Singleton(*args, **kwargs)
        return Singleton._instance


def task(arg):
    obj = Singleton.instance()
    print(obj)
for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()
time.sleep(20)
obj = Singleton.instance()
print(obj)

方法四:使用__new__(推荐)

要点:
1. __obj为保存的唯一变量
2. __init_flag是为了保证__init__函数只能调用一次,如果没有这个值,__init__会调用多次。

#!/bin/python

class MySingleton:
    __obj = None
    __init_flag = True
    def __new__(cls, *args, **kwargs):
        if cls.__obj == None:
            cls.__obj = object.__new__(cls)
        return cls.__obj

    def __init__(self, name):
        if MySingleton.__init_flag:
            self.name = name
            MySingleton.__init_flag = False


a = MySingleton("aa")
b = MySingleton("bb")
c = MySingleton("cc")

print (a)
print (b)
print (c)

运行结果:

[root@localhost ]# python3 new_singleton.py
<main.MySingleton object at 0x7f40e93645f8>
<main.MySingleton object at 0x7f40e93645f8>
<main.MySingleton object at 0x7f40e93645f8>

问题:使用python2运行时地址不同,原因??

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值