Python设计模式(一)--单例模式

单例模式

近日学习了设计模式相关数据,根据自身使用语言的特性,实现设计模式的案例。

概念

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

(一) 线程不安全的单例模式

由于python没有private、public等写法,所以要实现单例模式,需要重构类的__new__方法。
在Python中,__init__方法常用创建类对象的初始化。__new__方法创建对象实例。所以通过修改__new__方法,不让其调用__init__方法即可。如下:

#-*- coding:utf-8 -*-
#    文件名 :   single_instance.py
#    作者   :   WangYi
__author__ = 'WangYi'
import threading

class SingleServer(object):
    _instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            if not cls._instance:
                cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
        return cls._instance

def run(n):
    print("Treading_%d SingleServer id:%d" % (n, id(SingleServer())))


def main():
    t1 = threading.Thread(target=run, args=(1,))
    t2 = threading.Thread(target=run, args=(2,))
    t3 = threading.Thread(target=run, args=(3,))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print("End")
if __name__ == "__main__":
    main()

结果如下:

Treading_1 SingleServer id:140054791900112
Treading_2 SingleServer id:140054791900112
Treading_3 SingleServer id:140054791900112
End

(二)线程安全的单例模式

在多线程的模式下,有可能A向内存申请创建实例A’,与其同时B也这在向内存中创建B’。因为两者同时进行完成创建实例条件,则最终两者获得了两个不同的实例。导致后续的错误。
时序图如下:

Created with Raphaël 2.1.0 A A 内存 内存 B B 申请实例对象 申请实例对象 与A同时申请 产生实例对象B' 产生实例对象A'

由于CPython的GIL锁的原因,Python线程并未能够真正的发挥线程优势,尤其是在多核情况下,CPU也只能达到单核100%。

可以使用threading自带的Lock进行线程锁,保证资源的申请是安全的。修正的地方如下

import threading

Lock = threading.Lock()

class SingleServer(object):
    _instance = None

    def __init__(self):
        pass

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            try:
                Lock.acquire()   # double lock
                if not cls._instance:
                    cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
            finally:
                Lock.release()
        return cls._instance


def run(n):
    print("Treading_%d SingleServer id:%d" % (n, id(SingleServer())))


def main():
    t1 = threading.Thread(target=run, args=(1,))
    t2 = threading.Thread(target=run, args=(2,))
    t3 = threading.Thread(target=run, args=(3,))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print("End")


if __name__ == "__main__":
    main()

带参数的单例模式

发现提供的案例不够满足大家的需求,又添加了新的案例供大家参考。

import threading

Lock = threading.Lock()

class SingleServer(object):
    _instance = None

    def __init__(self, name, sex):
        self.name = name
        self.sex = sex

    def __new__(cls, *args, **kwargs):
        if not cls._instance:
            try:
                Lock.acquire()   # double lock
                if not cls._instance:
                    cls._instance = super(SingleServer, cls).__new__(cls, *args, **kwargs)
                    print("create instance")
            finally:
                Lock.release()
        return cls._instance

    def printinfo(self):
        """
        打印参数
        """
        print("My name is %s" % self.name)
        print("I'm a %s" % self.sex)


def run(n, name, sex):
    single = SingleServer(name, sex)
    print("Treading_%d SingleServer id:%d" % (n, id(single)))
    single.printinfo()


def main():
    t1 = threading.Thread(target=run, args=(1, 'Test1', 'Male'))
    t2 = threading.Thread(target=run, args=(2, 'Test2', 'Female'))
    t3 = threading.Thread(target=run, args=(3, 'Test3', 'Male'))
    t1.start()
    t2.start()
    t3.start()
    t1.join()
    t2.join()
    t3.join()
    print("End")


if __name__ == "__main__":
    main()

结果表明:

create instance
Treading_1 SingleServer id:139799877954320
My name is Test1
I'm a Male
Treading_2 SingleServer id:139799877954320
My name is Test2
I'm a Female
Treading_3 SingleServer id:139799877954320
My name is Test3
I'm a Male
End

采用单例模式下,给SingleServer传递的参数以最后一次传递的参数为准。即__init__函数依然有效。可提供用户进行初始化。

你可能感兴趣的:(设计模式)