python语言线程标准库threading.local源码解读

本段源码可以学习的地方:

1. 考虑到效率问题,可以通过上下文的机制,在属性被访问的时候临时构建;

2. 可以重写一些魔术方法,比如 __new__ 方法,在调用 object.__new__(cls) 前后进行属性的一些小设置;

3. 在本库中使用的重写魔术方法,上下文这两种基础之上,我们可以想到函数装饰器,类装饰器,异常捕获,以及两种上下文的结构;

灵活运用这些手法,可以让我们在代码架构上更上一层,能够更加省时省力。

  1 from weakref import ref  # ref用在了构造大字典元素元组的第一个位置即 (ref(Thread), 线程字典)
  2 from contextlib import contextmanager  # 上下文管理,用来确保__dict__属性的存在
  3 from threading import current_thread, RLock
  4 __all__ = ["local"]
  5
  6 class _localimpl:  # local()._local__impl = _localimpl()  # local()实例的属性_local__impl就是这个类的实例
  7     """一个管理线程字典的类"""
  8     __slots__ = ‘key‘, ‘dicts‘, ‘localargs‘, ‘locallock‘, ‘__weakref__‘  # _local__impl有这么多属性
  9
 10     def __init__(self):
 11         # 这个self.key是用在线程对象的字典中的key
 12         # self.key使用的一个字符串,这样既能运行的快,
 13         # 但是通过‘_threading_local._localimpl.‘ + str(id(self)也能保证不会冲突别的属性
 14
 15         self.key = ‘_threading_local._localimpl.‘ + str(id(self))
 16         #
 17         self.dicts = {}  # 大字典
 18         # 格式是: { id(线程1):(ref(Thread), 线程1自身的字典), id(线程2):(ref(Thread), 线程2自身的字典), ... }
 19
 20     def get_dict(self):  # 从大字典中拿(ref(Thread), 线程字典), 然后取线程字典
 21         thread = current_thread()
 22         return self.dicts[id(thread)][1]
 23
 24     def create_dict(self):  # 为当前线程创建一个线程字典,就是(ref(Thread), 线程字典)[1],即元组的第二部分
 25         localdict = {}
 26         key = self.key  # key使用‘_threading_local._localimpl.‘ + str(id(self)
 27         thread = current_thread()  # 当前线程
 28         idt = id(thread)  # 当前线程的id
 29         def local_deleted(_, key=key):  # 这个函数不看  pass
 30             # When the localimpl is deleted, remove the thread attribute.
 31             thread = wrthread()
 32             if thread is not None:
 33                 del thread.__dict__[key]
 34         def thread_deleted(_, idt=idt):  # 这个函数不看 pass
 35             # When the thread is deleted, remove the local dict.
 36             # Note that this is suboptimal if the thread object gets
 37             # caught in a reference loop. We would like to be called
 38             # as soon as the OS-level thread ends instead.
 39             local = wrlocal()
 40             if local is not None:
 41                 dct = local.dicts.pop(idt)
 42         wrlocal = ref(self, local_deleted)
 43         wrthread = ref(thread, thread_deleted)  # 大字典中每一个线程对应的元素的第一个位置: (ref(Thread), 小字典)
 44         thread.__dict__[key] = wrlocal
 45         self.dicts[idt] = wrthread, localdict  # 在大字典中构造: id(thread) : (ref(Thread), 小字典)
 46         return localdict
 47
 48
 49 @contextmanager
 50 def _patch(self):
 51     impl = object.__getattribute__(self, ‘_local__impl‘)  # 此时的self是local(), 拿local()._local__impl
 52     try:
 53         dct = impl.get_dict()   # 然后从拿到的local()._local__impl调用线程字典管理类的local()._local__impl.get_dict()方法
 54                                 # 从20行到22这个get_dict()方法的定义可以看出来,拿不到会报KeyError的
 55
 56     except KeyError:  # 如果拿不到报 KeyError之后捕捉
 57         dct = impl.create_dict()  # 然后再通过线程字典管理类临时创建一个
 58         args, kw = impl.localargs  # 这个时候把拿到
 59         self.__init__(*args, **kw)
 60     with impl.locallock:  # 通过上下文的方式上锁
 61         object.__setattr__(self, ‘__dict__‘, dct)  # 给local() 实例增加__dict__属性,这个属性指向大字典中value元组的第二个元素,即线程小字典
 62         yield  # 到目前为止,local()类的两个属性都构造完成
 63
 64
 65 class local:  # local类
 66     __slots__ = ‘_local__impl‘, ‘__dict__‘  # local类有两个属性可以访问
 67
 68     def __new__(cls, *args, **kw):
 69         if (args or kw) and (cls.__init__ is object.__init__):  # pass不看
 70             raise TypeError("Initialization arguments are not supported")
 71         self = object.__new__(cls)  # pass不看
 72         impl = _localimpl()  # _local_impl属性对应的是_localimpl类的实例
 73         impl.localargs = (args, kw)  # _local_impl属性即_localimpl类的实例 的 localargs属性是一个元组
 74         impl.locallock = RLock()  # pass 不看
 75         object.__setattr__(self, ‘_local__impl‘, impl)
 76         # 把_local__impl 增加给local(), 所以:local()._local__impl is ipml 即 _localimp()
 77
 78         # __slots__规定了local()有两个属性,这里已经设置了一个_local__impl;
 79         # 第二个属性__dict__当我们以后在访问的时候使用上下文进行临时增加,比如第85行
 80
 81         impl.create_dict()  # 就是local._local__impl.create_dict()
 82         return self  # 返回这个配置好_local__impl属性的local()实例
 83
 84     def __getattribute__(self, name):  # 当我们取local()的属性时
 85         with _patch(self):  # 会通过上下文先把数据准备好
 86             return object.__getattribute__(self, name)  # 在准备好的数据中去拿要拿的属性name
 87
 88     def __setattr__(self, name, value):
 89         if name == ‘__dict__‘:  # 这个判断语句是控制local()实例的__dict__属性只能读不能被替换
 90             raise AttributeError(
 91                 "%r object attribute ‘__dict__‘ is read-only"
 92                 % self.__class__.__name__)
 93         with _patch(self):  # 同理, 通过上下文先把__dict__构造好
 94             return object.__setattr__(self, name, value)  # 然后调用基类的方法设置属性
 95
 96     def __delattr__(self, name):  # 删除属性,同理,和__setattr__手法相似
 97         if name == ‘__dict__‘:   # 这个判断语句是控制local()实例的__dict__属性只能读不能被替换
 98             raise AttributeError(
 99                 "%r object attribute ‘__dict__‘ is read-only"
100                 % self.__class__.__name__)
101         with _patch(self):  # 同理, 通过上下文先把__dict__构造好
102             return object.__delattr__(self, name)
103
104 # 整体架构图:
105 ‘‘‘
106
107                                                                                / ——  key 属性
108                                                                               /  ——  dicts 属性, 格式{id(Thread):(ref(Thread), 线程小字典)}
109                         ———— : _local__impl属性   ----------  是_local类的实例                                                     |
110                       /                                                          —— 其他属性...                                    |
111                      /                          /—————————————————————————————————————————————————————————————————————————————————/
112     创建一个local实例                           /
113                      \                        /
114                       \                     /
115                         ———— : __dict__属性  --------  对应的是_local__impl属性的dicts 中的线程小字典
116
117
118
119 ‘‘‘

原文地址:https://www.cnblogs.com/duanming/p/11829354.html

时间: 2024-08-13 20:15:05

python语言线程标准库threading.local源码解读的相关文章

python线程threading.Timer源码解读

threading.Timer的作用 官方给的定义是: """Call a function after a specified number of seconds: t = Timer(30.0, f, args=None, kwargs=None) t.start() t.cancel() # stop the timer's action if it's still waiting """ 意思是: 在一个特定的秒数之后调用一个函数,使用方

threading.local()源码分析

前段时间写了个多线程的程序,了解到Python中有个与众不同的thread.local()方法,可以创建一个全局对象,各个线程可以用这个全局对象保存各自的局部变量,而在使用时不受其他线程的影响.于是抽时间分析了一下thread.local()方法的源码. 相关知识储备: __slots__变量:__slots__变量一个元组,可以限制该类可使用的成员变量,不在__slots__变量中的成员变量名不能被动态添加到该类中. 参考:https://www.cnblogs.com/zhaoshizi/p

Python语言和标准库(第一章:做出决策)

1-做出决策 2-函数 3-类与对象 4-组织程序 5-文件和目录 6-Python语言的其他特性 7-创建模块 8-文本处理 第一章:做出决策 本章介绍如何创建可以使用循环重复相同操作的情形,自动逐步遍历列表,元组和字典. 如何使用字典,列表和元组,探究字典中的内容. 使用一场处理编写的程序,以应对可能在程序内部处理的问题. 1.1比较两个值是否相等 在python里,如果每个序列中同一位置的每个元素都相同,那么两个序列相同.字典的比较和序列的一样. 1.2比较两个值是否不同 != 1.3比较

Python语言和标准库(第七章:文本处理)

7.1文本处理的用途 总的来说,文本处理背后的全部思想是找到目标文本.当然,有的情况下数据以结构化的方式组织,这就是所谓的数据库.然而有些数据资源包含的信息完全不是有序和规整的,例如成百上千的文件的目录结构.当需要查找这种类型的数据或者以某种方式处理它们,文本处理就非常实用.也可以同RDBMS(关系数据库管理系统)结合起来查找. 文本处理领域的两个主要的工具是目录导航和一种称为正则表达式的神奇技术. 目录导航:是一个不同的操作系统真正给简单的程序带来大量麻烦的领域,因为三个主要的操作系统的家族都

go标准库-log包源码学习

log包是go语言提供的一个简单的日志记录功能,其中定义了一个结构体类型 Logger,是整个包的基础部分,包中的其他方法都是围绕这整个结构体创建的. Logger结构 Logger结构的定义如下: type Logger struct { mu sync.Mutex prefix string flag int out io.Writer buf []byte } mu 是sync.Mutex,它是一个同步互斥锁,用于保证日志记录的原子性. prefix 是输入的日志每一行的前缀 flag 是

Go语言开发(十二)、Go语言常用标准库二

Go语言开发(十二).Go语言常用标准库二 一.os 1.os简介 os 包提供了不依赖平台的操作系统函数接口,设计像Unix风格,但错误处理是go风格,当os包使用时,如果失败后返回错误类型而不是错误数量. 2.os常用接口 func Hostname() (name string, err error) // Hostname返回内核提供的主机名 func Environ() []string // Environ返回表示环境变量的格式为"key=value"的字符串的切片拷贝 f

Go语言开发(十一)、Go语言常用标准库一

Go语言开发(十一).Go语言常用标准库一 一.log 1.log模块简介 Go语言中log模块用于在程序中输出日志.log模块提供了三类日志输出接口,Print.Fatal和Panic.Print是普通输出:Fatal是在执行完Print后,执行 os.Exit(1):Panic是在执行完Print后调用panic()方法.log模块对每一类接口其提供了3中调用方式,分别是"Xxxx. Xxxxln.Xxxxf". 2.log.Print接口 log.Print类接口包括log.Pr

Go语言开发(十三)、Go语言常用标准库三

Go语言开发(十三).Go语言常用标准库三 一.sync 1.sync简介 sync提供基本的同步原语,如sync.Mutex,sync.RWMutex,sync.Once,sync.Cond,sync.Waitgroup,除了Once和WaitGroup类型外,大多数类型都供低级库使用.Go语言中,不要通过共享内存通信,而要通过通信共享内存,通过Channel和沟通可以更好地完成更高级别的同步. type Locker interface { Lock() Unlock() } Locker提

Go语言开发(十四)、Go语言常用标准库四

Go语言开发(十四).Go语言常用标准库四 一.heap 1.heap简介 heap仅仅提供了最小堆的操作,没有提供堆的数据结构,堆的数据结构必须由开发者自己实现.heap提供了一个heap.Interface接口来作为堆的操作和堆的数据结构(开发者自己实现)之间的桥梁,堆的数据结构必须满足此接口: type Interface interface { sort.Interface Push(x interface{}) // add x as element Len() Pop() inter