此系列是对《阿里巴巴技术协会-Python与设计模式系列课程》学习笔记。
一、总线
总线是计算机各种功能不仅或者设备之间传送数据、控制信号等信息的公共通信解决方案之一。现在假设有如下场景:
某中央处理器(CPU)通过某种协议总心啊与一个信号灯相连,信号灯有64种颜色可以设置,中央处理器上运行着三个线程,都可以对这个信号灯进行控制,并且可以独立设置该信号灯的颜色。抽象掉协议细节(用打印表示),如何实现线程对信号灯的控制逻辑?
最先想到的无疑是加线程锁进行控制,但是各个线程对锁的控制,加大了模块之间的耦合。下面我们就用设计模式中的单例模式来解决问题。
二、单例模式
单例模式是指:保证仅有一个实例,并提供一个访问它的全局 访问点。具体到此例中,总线对象,就是一个单例,他仅有一个实例,各个线程对总线的访问只有一个全局访问点,即唯一的实例。
单例模式的优点:
- 由于单例模式要求在全局内只有一个实例,因而可以节省较多的内存空间;
- 全局只有一个接入点,可以很好的进行数据的同步控制,避免多重占用;
- 单例可长驻内存,减少系统开销
单例模式的缺点:
- 扩展比较困难
- 某种情况下会导致“资源瓶颈”
- 单例模式是并发协作软件模块中需要最先完成的,因为不利于测试
- 赋予单例太多职责,某种程度上违反单一职责原则
单例模式的应用举例:
- 生成全局唯一的序列号
- 访问全局复用的唯一资源,如磁盘、总线等
- 系统全局统一管理,如Windows下的Task Manager
- 网站计数器
三、代码如下:
#encoding = utf8
import threading
import time
#使用__new__方法来实现单例模式
class Singleton(object): #抽象单例
def __new__(cls, *args, **kw):
#把类实例绑定到类变量_instance上,如果cls._instance为None,
#表示该类还没有被实例化过,实例化该类并返回
#如果cls._instance不为None,表示该类已实例化直接返回cls._instance
if not hasattr(cls,'_instance'):
orig = super(Singleton, cls) #super()调用父类,创建实例属性
cls._instance =orig.__new__(cls,*args,**kw)
return cls._instance
#总线
class Bus(Singleton):
lock = threading.RLock() #加载线程的锁对象(多重锁,在同一线程中可被多次acquire)
def sendData(self,data):
self.lock.acquire() #获取总线控制权
time.sleep(3)
print('Sending Single Date...',data)
self.lock.release() #释放线程锁
#线程对象,为更加说明单例的含义,这里将bus对象实例化写在run里
class VisitEntity(threading.Thread):
my_bus = ""
name = ""
def getName(self):
return self.name
def setName(self, name: str):
self.name = name
def run(self):
self.my_bus = Bus()
self.my_bus.sendData(self.name)
if __name__ == '__main__':
for i in range(3):
print("Entity %d begin to run..."%i)
my_entity = VisitEntity()
my_entity.setName("Entity_"+str(i))
my_entity.start() #start()方法用来启动多线程
在程序的运行中,三个线程同时运行(运行结果的前三行很快被打印出来),然后分别占用总线资源(后三行每隔3秒打印一行)。虽然看上去总线Bus被实例化了三次,但实际上在内存里只有一个实例。(因为__new__()方法会进行判断,实例化一次后_instance属性就会存在,不会进入if模块执行)
四、知识点
1、静态方法、普通方法、类方法
- 静态方法:①静态方法是类中的函数,不需要实例(不需要self参数)
②主要用来存放逻辑性的代码,与类本身没有交互,即静态方法中不会涉及到类内的方法和属性,若想调用类内的方法和属性则可通过 __class__.类属性。
③形式:添加@staticmethod装饰器
④调用:直接类名调用或者实例化调用
注意:在Python 2 中,如果一个类的方法不需要self参数,必须声明为静态方法,即加上@staticmethod装饰器,从而不带实例调用它。python3中,如果一个类的方法不需要self参数,不再需要声明为静态方法,但是这样的话只能通过类去调用这个方法,如果使用实例去调用方法会引发异常,因为会自动传入一个self参数
- 类方法:①类方法传入的第一个参数是cls,是类本身。
②类方法可以通过类直接调用,过通过实例直接调用
③形式:添加@classmethod装饰器
- 实例方法①除了静态方法和类方法外,类的其他方法都属于实例方法
②实例方法需要将类实例化后调用,如果使用类直接带哦用实例方法,需要显式的将实例作为参数传入
2、new()方法
Python中存在于类里面的构造方法__init__()负责将类实例化,而在__init__()启动之前,__new__()决定是否要使用该__init__()方法,因为__new__()可以调用其他类的构造方法或者返货别的对象来作为本类的实例。(如以上代码中 return cls._instance)