深入单例模式

class Singleton(object):

    def __init__(self):
        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 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>
<__main__.Singleton object at 0x000002B9D0BE0240>

一般情况,大家以为这样就完成了单例模式,但是这样当使用多线程时会存在问题

在__init__添加模拟在做io操作:

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

会出现:

<__main__.Singleton object at 0x0000016F611C18D0>
<__main__.Singleton object at 0x0000016F61040278>
<__main__.Singleton object at 0x0000016F611F5F28>
<__main__.Singleton object at 0x0000016F611F5E80>
<__main__.Singleton object at 0x0000016F61204C18>
<__main__.Singleton object at 0x0000016F611C19B0>
<__main__.Singleton object at 0x0000016F6120C198>
<__main__.Singleton object at 0x0000016F6121A6A0>
<__main__.Singleton object at 0x0000016F6121A668>
<__main__.Singleton object at 0x0000016F6120C278>

这是因为多线程的线程锁的问题。

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

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

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

    @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 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>
<__main__.Singleton object at 0x00000222EEB830F0>

现在有个问题需要优化,就是一开始开多线程创建10个单例,加锁是为了数据安全,创建完后再过五秒,此时再创建对象时先判断之前有没有实例对象有的话直接调用之前创立的对象,不需要线程锁,影响效率所以稍微改写。

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(1)
print("wait 5 seconds")
time.sleep(5)
obj = Singleton.instance()
print(obj)
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
<__main__.Singleton object at 0x000001FEE03010F0>
wait 5 seconds
<__main__.Singleton object at 0x000001FEE03010F0>

这种方式实现的单例模式,使用时会有限制,以后实例化必须通过 obj = Singleton.instance() 

如果用 obj=Singleton() ,这种方式得到的不是单例

通过上面例子,我们可以知道,当我们实现单例时,为了保证线程安全需要在内部加入锁

我们知道,当我们实例化一个对象时,是先执行了类的__new__方法(我们没写时,默认调用object.__new__),实例化对象;然后再执行类的__init__方法,对这个对象进行初始化,所有我们可以基于这个,实现单例模式

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

    def __init__(self):
        time.sleep(1)
    def __new__(cls, *args, **kwargs):
        if not hasattr(Singleton,"__instance"):
            with Singleton._instance_lock:#加锁
                if not hasattr(Singleton, "_instance"):
                    Singleton._instance = super().__new__(cls)
        return Singleton._instance


def task(arg):
    obj = Singleton()
    print(obj)
for i in range(10):
    t = threading.Thread(target=task,args=[i,])
    t.start()
time.sleep(2)
print("wait 5 seconds")
time.sleep(5)
obj = Singleton()
print(obj)
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
<__main__.Singleton object at 0x0000018A229F2160>
wait 5 seconds
<__main__.Singleton object at 0x0000018A229F2160>

猜你喜欢

转载自blog.csdn.net/u014248032/article/details/84645905