Python多线程单例的几种实现方式

目录:

  • 单例模式在类中实现
  • 装饰器批量装饰实现单例模式 ,且不丢失类型提示
  • 限制实例个数

1.重写__new__方法实现多线程情况下的单例模式

用new方法实现单例模式

import time, threadingclass Singleton:"""单例模式————最多只允许创建一个该类实例"""_lock = threading.Lock()_instance = Nonedef __new__(cls, a, b, *args, **kwargs):time.sleep(1)   # 模拟高并发情况with cls._lock:# print("进入lock区域")if not cls._instance:cls._instance = object.__new__(cls)# cls._instance = super().__new__(cls)    # 与上一条作用相同# cls._instance.a = areturn cls._instance# S = Singleton(1, 2)
# print(S)def task(a, b):s = Singleton(a, b)print(s)for i in range(5):t = threading.Thread(target=task, args=(2, 3))t.start()

运行始终为单一实例:

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

2.使用装饰器实现多线程情况下的单例模式

想要批量给多个类实现单例模式,自然少不了装饰类的装饰器
先介绍第一种方法,线程锁的方式和前面的稍有不一样,没有使用类的内部属性记录实例对象,而是使用了字典键唯一的特性,将类对象Example作为键,实例化后的对象作为值

2.1 第一种实现方式(不推荐使用)
错误示例:

import time
from functools import wraps 
import threadingdef singleton_dec(class_):map_ = {}	# 定义一个字典,类本身作为键,实例化后的对象作为其相应的值@wraps(class_)	# (顺便复习一下装饰器,将被装饰的函数改回原来的属性和名字)def singleton(*args):# print(class_.__name__)# print(class_.foo.__name__)if class_ not in map_:  # 如果类没有在字典就实例化time.sleep(1)	# 没有加锁的情况下模拟多线程的情况map_[class_] = class_(*args)  # 实例化类方法并存到字典中return map_[class_]  # 字典中有这个类就不实例化return singleton@singleton_dec
class EXAMPLE:def __init__(self, param='abc'):self.param = paramdef foo(self):pass# E1 = EXAMPLE('123')
# print(E1.param)
# print(E1)
# E2 = EXAMPLE('456')  # 由于EXAMPLE已经实例化了(在map_中),这一步并没有对EXAMPLE做出任何改变
# print(E2.param)  # 输出的依然是123
# print(E2)def task():e = EXAMPLE()print(e)for i in range(5):t = threading.Thread(target=task)t.start()

加上sleep模拟多线程的执行结果:

<__main__.EXAMPLE object at 0x000001BD894A7128>
<__main__.EXAMPLE object at 0x000001BD8949E710>
<__main__.EXAMPLE object at 0x000001BD894A1DD8>
<__main__.EXAMPLE object at 0x000001BD8949EB00>
<__main__.EXAMPLE object at 0x000001BD8949E710>

可以看到在并发情况很高的时候并没有实现单例化

因为方法二没有像方法一一样设置线程锁,所以这里会出现创建多个实例的情况

想要实现单例,也需要像方法一中一样加上线程锁

不过是给字典方法加上一个线程锁:
示例:

# -*- coding: utf-8 -*-
import threading, time
from functools import wrapsclass DictWithLock(dict):lock = threading.Lock()def __init__(self, *args, **kwargs):super().__init__(*args, **kwargs)def singleton_decorator(callable_obj):dl = DictWithLock()@wraps(callable_obj)def inner(*args, **kwargs):# print('测试', *args, **kwargs)time.sleep(1)with dl.lock:   # 一次只能有一个线程使用DictWithLock的lock属性# print("进入lock区域")if not dl.get("_instance"):dl["_instance"] = callable_obj(*args, **kwargs)return dl["_instance"]return inner@singleton_decorator
class Test:def __init__(self, a=0, b=1):print('Test.test', a, b)def task(a, b):t = Test(a, b)print(t)		# t.a时不会有提示# t0 = Test(2, 3)
# print(t0)for i in range(5):tsk = threading.Thread(target=task, args=(2, 3))tsk.start()

输出结果:

Test.test 2 3
<__main__.Test object at 0x00000292250AB070>
<__main__.Test object at 0x00000292250AB070><__main__.Test object at 0x00000292250AB070><__main__.Test object at 0x00000292250AB070><__main__.Test object at 0x00000292250AB070>已结束,退出代码 0

线程各自异步执行,但始终只会创建一个Example的实例

这种方式是按照一般实现装饰器的方式实现,不过这种装饰器有明显的缺点,被装饰过的类不会有类型提示
于是祭出第二种类的装饰器写法

2.2 第二种实现方式
被装饰过的类不会丢失类型提示

# -*- coding: utf-8 -*-
import threadingdef Singleton(cls):# print("log singleton")cls._instance = Nonecls._lock = threading.Lock()def __new__(*args, **kwargs): # print(f"log new", args, kwargs)     # 实际上args中也包含cls和相关的参数with cls._lock:if not cls._instance:cls._instance = object.__new__(cls)return cls._instancecls.__new__ = __new__return cls@Singleton
class T:def __init__(self, a):self.a = adef t1():t = T(1)tt = T(2)print(t, t.a)		# t.a时有提示print(tt, t.a)def t11():def task(a):s = T(a)print(s)for i in range(5):t = threading.Thread(target=task, args=(2, ))t.start()if __name__ == '__main__':# t1()t11()

输出:

<__main__.T object at 0x000001EFE5977850>
<__main__.T object at 0x000001EFE5977850>
<__main__.T object at 0x000001EFE5977850>
<__main__.T object at 0x000001EFE5977850>
<__main__.T object at 0x000001EFE5977850>

限制实例个数的模式————limit实例模式

__new__()方法实现最多创建3个实例的模式:

class LimitedInstances:"""限制实例个数的模式————limit实例模式"""_instances = []limit = 3def __new__(cls, *args, **kwargs):if not len(cls._instances) < cls.limit:  # 若个数达到limit说明已满raise Exception(f"Can not create instance more than {cls.limit}.")instance = super().__new__(cls)cls._instances.append(instance)return instancedef __delete__(self):self._instances.remove(self)LI1 = LimitedInstances(1, 2)
LI2 = LimitedInstances(2, 2)LI3 = LimitedInstances(3, 2)
LI3.__delete__()
LI4 = LimitedInstances(4, 2)
print(f"删除一个又加进来一个,还是3个\n", LimitedInstances._instances)
# LI5 = LimitedInstances(5, 2)  # 超过3个会报错

运行结果,限制最多3个实例:

删除一个又加进来一个,还是3个实例[<__main__.LimitedInstances object at 0x00000278A03BDE20>, <__main__.LimitedInstances object at 0x00000278A03BDDC0>, <__main__.LimitedInstances object at 0x00000278A03BD520>]

模拟高并发情况下,还是可能会出现超过3个实例的情况:

import time, threadingclass LimitedInstances:"""限制实例个数的模式————limit实例模式"""_instances = []limit = 3def __new__(cls, *args, **kwargs):if not len(cls._instances) < cls.limit:  # 若个数达到limit说明已满raise Exception(f"Can not create instance more than {cls.limit}.")instance = super().__new__(cls)time.sleep(1)   # 模拟高并发情况cls._instances.append(instance)return instancedef __delete__(self):self._instances.remove(self)def task():LI = LimitedInstances()print(LI)def task():LI = LimitedInstances()print(LI)for i in range(6):t = threading.Thread(target=task, args=())t.start()
time.sleep(2)
print(len(LimitedInstances._instances), LimitedInstances._instances)

运行结果,出现了6个不同的实例的情况:

<__main__.LimitedInstances object at 0x0000027066EC8DF0><__main__.LimitedInstances object at 0x0000027066EED370><__main__.LimitedInstances object at 0x0000027066EC8BB0>
<__main__.LimitedInstances object at 0x0000027066EED0D0>
<__main__.LimitedInstances object at 0x0000027066EC8610>
<__main__.LimitedInstances object at 0x0000027066EED610>
6 [<__main__.LimitedInstances object at 0x0000027066EC8DF0>, <__main__.LimitedInstances object at 0x0000027066EED370>, <__main__.LimitedInstances object at 0x0000027066EC8BB0>, <__main__.LimitedInstances object at 0x0000027066EED0D0>, <__main__.LimitedInstances object at 0x0000027066EC8610>, <__main__.LimitedInstances object at 0x0000027066EED610>]

解决方式,同之前,加上线程锁:

import time, threadingclass LimitedInstances:"""限制实例个数的模式————limit实例模式"""_instances = []limit = 3_lock = threading.Lock()def __new__(cls, *args, **kwargs):with cls._lock:  # 一次只能一个线程执行实例个数判断和实例存入if not len(cls._instances) < cls.limit:  # 若个数达到limit说明已满raise Exception(f"Can not create instance more than {cls.limit}.")instance = super().__new__(cls)time.sleep(1)  # 模拟高并发情况cls._instances.append(instance)return instancedef __delete__(self):self._instances.remove(self)def task():LI = LimitedInstances()print(LI)for i in range(6):t = threading.Thread(target=task, args=())t.start()
# time.sleep(2)
# print(len(LimitedInstances._instances), LimitedInstances._instances)

运行时,创建超过了3个实例会报错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/469457.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

图像目标分割_5 DeepLab V2 V3 V3+

6.5.1 DeepLab V2 6.5.1.1 改变特点 atrous convolution采用ASPP ( atrous spatial pyramid pooling) 多尺度获得更好的分割效果合并深度卷积网络和概率图模型方法&#xff0c;增强对物体边界的定位。基础层由VGG16转为ResNet 和v1不同&#xff1a; 通过多尺度输入处理或者多…

jQuery / zepto ajax 全局默认设置

jQuery / zepto 的 $.ajax 方法需要配置很多选项, 有些是很常用的每个 ajax 请求都要用到的, 可以全局设置, 避免每次都写. 注意: 此处用的 jQuery 版本是 1.8.3, zepto 版本是 1.1.6 和 1.2.0. 使用别的版本的要自己测试下. jQuery 的方法是 jQuery.ajaxSetup() $.ajaxSetup(…

Python导包、模块报错的问题

import报错No module named "xxx"的问题 如何将指定目录作为项目根目录&#xff0c;让项目根目录下的包/模块都可以直接导入&#xff1f;&#xff08;linux下&#xff09; Python导入模块时&#xff0c;解释器如何定位模块&#xff1a; 1.当前目录 2.内置模块列表 3…

CC2540 串口0 通道2配置

从图里面可以看出来&#xff0c;串口0有两个通道&#xff0c;一个通道是P02 P03两个GPIO口。 还有一个通道是P14 P15两个GPIO口。 在软件配置的时候&#xff0c;主要是配置的是一个通道相关的寄存器。 7.6.4 USART 0 The SFR register bit PERCFG.U0CFG selects whether to u…

图像目标分割_6 Mask RCNN

6.6.0 背景 目标检测和语义分割的效果在短时间内得到了很大的改善。在很大程度上&#xff0c;这些进步是由强大的基线系统驱动的&#xff0c;例如&#xff0c;分别用于目标检测和语义分割的Fast/Faster R-CNN和全卷积网络(FCN)框架。这些方法在概念上是直观的&#xff0c;提供…

cf 621E. Wet Shark and Blocks

神奇&#xff0c;矩阵乘法23333333333333333 递推式是很简单的&#xff08;连我这种不会DP的人都写出来了。&#xff09; 需要求出的是转移矩阵&#xff08;还是叫系数矩阵的&#xff09;&#xff0c;也是最这个东西用快速幂。 这个东西的i&#xff0c;j大概就表示从i到j的方案…

Python enum的使用总结

Python enum的使用总结 枚举(enumeration)在许多编程语言中常被表示为一种基础的数据结构使用&#xff0c;枚举帮助组织一系列密切相关的成员到同一个群组机制下&#xff0c;一般各种离散的属性都可以用枚举的数据结构定义&#xff0c;比如颜色、季节、国家、时间单位等 在Pyt…

ascii码转字符

在C语言中&#xff0c;ASCII码转字符非常简单。但是之前因为没有用到就没有去关注这方面的问题。 printf("%c\n",char(69)); E 下面是ASCII码表&#xff1a; BinDecHex缩写/字符解释0000 0000000NUL(null)空字符0000 0001101SOH(start of headline)标题开始0000…

SCI论文写作训练营笔记汇总01_概述+文献检索与管理

1 概述 1.1 适用人群 ①初涉科研&#xff0c; 目前或将来有英文科技论文发表需求的科研工作者 ②正在撰写或准备撰写英文科技论文的科研工作者 1.2 科技论文的基本结构 1.3 科技论文组成部分的写作方法 1.4 阅读文献的重要性 2、文献检索与管理 2.1 如何查找文献参考 2.2 文…

天猫11.11:搜索引擎实时秒级更新(转载)

搜索是很多用户在天猫购物时的第一入口&#xff0c;搜索结果会根据销量、库存、人气对商品进行排序&#xff0c;而商品的显示顺序往往会决定用户的选择&#xff0c;所以保证搜索结果的实时性和准确性非常重要。在电商系统中&#xff0c;特别是在“双十一”这样的高并发场景下&a…

pandas处理日期的几种常用方法

1.读取字符串日期 写入csv 文件 csv_text """date, value 2022-01-01, 1 2022-01-05, 5 2022-11-05, 5 """ with open("date_text.csv", "w") as f:f.write(csv_text)读取日期 df pd.read_csv("date_text.csv"…

OAD 空中升级

http://www.deyisupport.com/question_answer/wireless_connectivity/bluetooth/f/103/p/69222/172351.aspx#172351&#xfeff;&#xfeff;第二十三节 OAD空中升级 通过仿真器更新程序或者通过USB更新固件那都是一般人都可以实现的操作&#xff0c;但是要想实现OAD空中升级…

SCI论文写作训练营笔记汇总02_英文科技论文阅读与解析

3、英文科技论文阅读与解析的方法 3.1 科技论文介绍 3.1.1 科技论文的类型 • Research • Review • Theoretical • Methodological • Case study 3.1.2 研究型论文的结构 3.1.3 科技论文的基本结构 3.2 文献阅读 3.2.1 文献选择的原则 3.2.2 文献阅读顺序 3.2.2 文献阅读…

9.带有返回值的函数

<!DOCTYPE html><html><body> <p>本例调用的函数会执行一个计算&#xff0c;然后返回结果&#xff1a;</p> <p id"demo"></p> <script>function myFunction(a,b){return a*b;} document.getElementById("demo&…

报错curl: (7) Failed to connect to 127.0.0.1 port xxxx: Connection refused

&#xff08;pyenv install xxx&#xff09; 报错curl: (7) Failed to connect to 127.0.0.1 port xxxx: Connection refused的解决方法 问题重现截图&#xff1a; 在查看下面的原因和使用解决方法之前&#xff0c;确保自己的pyenv已经安装好了最新的python-build&#xff0c…

linux通过I2C地址查看设备名称

 root@android:/sys/bus/i2c # cd devices cd devices root@android:/sys/bus/i2c/devices # ls ls 0-0020 0-0022 0-0036 0-0078 1-000c 1-000d 1-001d 1-0028 1-0029 1-002a 1-0038 1-0060 1-0068 2-001c i2c-0 i2c-1 i2c-2 root@android:/sys/bus/i2c/devices # cd 0-00…

SCI论文写作训练营笔记汇总03_科技论文写作(方法篇)

4、科技论文写作——方法篇 4.1 准备工作与概述 4.1.2 杂志编辑评估文章的标准 4.2 图表部分的写作方法 4.2.1 为什么使用图表 4.2.2 如何使用图表 4.2.3 如何选择图or表or文字 4.2.4 图片/表格制作软件 4.2.5 图片的各种指标 4.2.6 图片的分类 4.2.6 图注的使用 4.2.7 表格 …

跨域获取

本地&#xff1a; <?php$_arr array(a>1,b>2,c>3);$_result json_encode($_arr);echo $_result; ?> //本地获取$(form input[typebutton]).click(function(){$.ajax({type:"post",url:"test.php",async:true,dataType:json,success:fun…

python 嵌套型partials(nested partials)的使用

Python嵌套型partial的使用 partial对象中包含partial对象的使用 要实现的目标&#xff0c;简单示例&#xff1a; from functools import partialdef func1(f):return fdef func2(f1):return f1def func(n):return np partial(func2, partial(func1, partial(func, 5))) pri…

SCI论文写作训练营笔记汇总04_科技论文写作(技巧篇)

1、语言点 1.1 时态 1.1.1 时态使用的基本原则 1.1.2 在引言部分的时态使用 1.1.3 在“材料与方法”部分的时态使用 1.1.4 在“结果”部分的时态使用 1.1.5 在“讨论”部分的时态使用 1.1.6 总结 1.2 语态 1.2.1 语态-主动/被动 1.2.2 语态-主动/被动的不同点 1.2.2 语态-主…