threading.local()源码分析
Posted zhaoshizi
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了threading.local()源码分析相关的知识,希望对你有一定的参考价值。
前段时间写了个多线程的程序,了解到Python中有个与众不同的thread.local()方法,可以创建一个全局对象,各个线程可以用这个全局对象保存各自的局部变量,而在使用时不受其他线程的影响。于是抽时间分析了一下thread.local()方法的源码。
相关知识储备:
__slots__变量:__slots__变量一个元组,可以限制该类可使用的成员变量,不在__slots__变量中的成员变量名不能被动态添加到该类中。
参考:https://www.cnblogs.com/zhaoshizi/p/9384430.html
Python上下文管理器contextmanager:引入contextlib模块中的contextmanager,使用contextmanager来注解一个函数,则可以用with语句来调用该函数,在函数中遇到yield时,在yield处停止,并把yield后的值返回。当执行完with的所有的语句后,再执行yield后面的语句。
参考:https://www.jb51.net/article/92387.htm
Python弱引用weakref:你可以通过调用weakref模块的ref(obj[,callback])来创建一个弱引用,obj是你想弱引用的对象,callback是一个可选的函数,当因没有引用导致Python要销毁这个对象时调用
参考:https://segmentfault.com/a/1190000005729873
下面进入正题,thread.local类原理分析(不同版本的Python该类的实现方式可以不同,本文以Python3.6为例):
thread.local类使用了一个_localimpl类,来作为各线程局部变量的管理类,该类的作用是保存各个线程的局部变量。那如何让各个线程的局部变量相互独立且不相互影响?没错,答案就是用字典类型变量。每个线程分配一个字典项,字典项的key为线程的id,value值保存的是该线程局部变量组成的另一个字典。_localimpl类中通过__slots__变量限制定义了几个成员变量,包括locallock(锁)和dicts(字典),dicts变量中就如上所述保存了各线程的局部变量构成的字典。所以dicts是字典的字典。
为了保证线程被删除时,对应的局部变量也会被删除,_localimpl类中还定义了一个该线程的弱引用wrthread,当该线路引用数为0时,调用回调函数thread_deleted删除dicts字典中该线程的记录项。
1 class _localimpl: 2 """A class managing thread-local dicts""" 3 """管理线程局部字典的类""" 4 __slots__ = ‘key‘, ‘dicts‘, ‘localargs‘, ‘locallock‘, ‘__weakref__‘ 5 6 def __init__(self): 7 # The key used in the Thread objects‘ attribute dicts. 8 # We keep it a string for speed but make it unlikely to clash with 9 # a "real" attribute. 10 self.key = ‘_threading_local._localimpl.‘ + str(id(self)) 11 # { id(Thread) -> (ref(Thread), thread-local dict) } 12 #字典变量中的值中以id(Thread)为key,当前线程的弱引用ref(Thread)和 13 #当前线程的局部变量字典(thread-local dict)组成的元组 14 self.dicts = {} 15 16 def get_dict(self): 17 """Return the dict for the current thread. Raises KeyError if none 18 defined.""" 19 """返回当前线程的局部变量字典,其是元组中的第二个元素""" 20 thread = current_thread() 21 return self.dicts[id(thread)][1] 22 23 def create_dict(self): 24 """Create a new dict for the current thread, and return it.""" 25 """为当前线程造建一个局部变量字典,用来保存局部变量""" 26 localdict = {} 27 key = self.key 28 thread = current_thread() 29 idt = id(thread) 30 def local_deleted(_, key=key): 31 # When the localimpl is deleted, remove the thread attribute. 32 # 定义一个弱引用的回调函数,当线程局部变量的管理类localimpl被删除时,从线程中删除对应的变量 33 thread = wrthread() 34 if thread is not None: 35 del thread.__dict__[key] 36 def thread_deleted(_, idt=idt): 37 # When the thread is deleted, remove the local dict. 38 # Note that this is suboptimal if the thread object gets 39 # caught in a reference loop. We would like to be called 40 # as soon as the OS-level thread ends instead. 41 # 定义一个弱引用的回调函数,当线程被删除时,在管理类localimpl对象的字典中删除该线程的字典项 42 local = wrlocal() 43 if local is not None: 44 dct = local.dicts.pop(idt) 45 #定义两个弱引用 46 wrlocal = ref(self, local_deleted) 47 wrthread = ref(thread, thread_deleted) 48 #线程和局部变量管理类相互关联 49 thread.__dict__[key] = wrlocal 50 #在字典中以线程id为key,保存了弱引用和线程局部变量的字典 51 self.dicts[idt] = wrthread, localdict 52 return localdict
1 #定义上下文管理器,用来做统一的预处理,把_local__impl类中的保存的该线程的字典值赋值为该线程的__dict__,local类就可以对局部变量进行操作了。 2 @contextmanager 3 def _patch(self): 4 #获取_local__impl值 5 impl = object.__getattribute__(self, ‘_local__impl‘) 6 try: 7 #获取该线程的局部变量字典 8 dct = impl.get_dict() 9 except KeyError: 10 dct = impl.create_dict() 11 args, kw = impl.localargs 12 self.__init__(*args, **kw) 13 #操作时加锁 14 with impl.locallock: 15 #将局部变量dict值赋值给__dict__ 16 object.__setattr__(self, ‘__dict__‘, dct) 17 #跳过_patch继续执行with语句块,执行完成后再执行yield后面的语句(退出) 18 yield 19 20 21 class local: 22 #只定义了两个变量,局部变量存在__dict__指向的字典里 23 __slots__ = ‘_local__impl‘, ‘__dict__‘ 24 25 def __new__(cls, *args, **kw): 26 if (args or kw) and (cls.__init__ is object.__init__): 27 raise TypeError("Initialization arguments are not supported") 28 self = object.__new__(cls) 29 impl = _localimpl() 30 impl.localargs = (args, kw) 31 impl.locallock = RLock() 32 object.__setattr__(self, ‘_local__impl‘, impl) 33 # We need to create the thread dict in anticipation of 34 # __init__ being called, to make sure we don‘t call it 35 # again ourselves. 36 impl.create_dict() 37 return self 38 39 #每个取值操作都用调用该函数 40 def __getattribute__(self, name): 41 #从局部变量管理类中获取该线程的局部变量字典,赋值给__dict__ 42 with _patch(self): 43 #从__dict__中获取局部变量值 44 return object.__getattribute__(self, name) 45 46 def __setattr__(self, name, value): 47 if name == ‘__dict__‘: 48 raise AttributeError( 49 "%r object attribute ‘__dict__‘ is read-only" 50 % self.__class__.__name__) 51 with _patch(self): 52 return object.__setattr__(self, name, value) 53 54 def __delattr__(self, name): 55 if name == ‘__dict__‘: 56 raise AttributeError( 57 "%r object attribute ‘__dict__‘ is read-only" 58 % self.__class__.__name__) 59 with _patch(self): 60 return object.__delattr__(self, name) 61 62 63 from threading import current_thread, RLock
以上是关于threading.local()源码分析的主要内容,如果未能解决你的问题,请参考以下文章
Android 插件化VirtualApp 源码分析 ( 目前的 API 现状 | 安装应用源码分析 | 安装按钮执行的操作 | 返回到 HomeActivity 执行的操作 )(代码片段
Android 逆向整体加固脱壳 ( DEX 优化流程分析 | DexPrepare.cpp 中 dvmOptimizeDexFile() 方法分析 | /bin/dexopt 源码分析 )(代码片段