同一个进程下,多个线程是共享进程的数据,多线程为了保证数据的安全性,多线程的写操作会加锁,加锁也就意味着多线程模型下,效率将降低。
threading.local()可以为每个线程创建局部名称空间,threading.local()是全局的,所有的线程都能访问,但是放入threading.local()对象中的属性数据不会被其它线程干扰.
threading.local实现线程数据隔离的主要原理,是通过一个大字典实现,大字典key值报存线程的id,value是一个小字典,保存该线程局部变量组成的字典。
知识储备:
1、slots__变量:
作用实例化时限制__dict__属性,每次实例化都需要新分配一个dict,因此在大量实例化该类的时候,存在空间浪费;如果类的属性确定,可通过__slots__变量来进行限制,配置__slots__后,只有__slots__中的属性能够动态增加和修改,不能再增加别的属性,不再有__dict
https://blog.csdn.net/sxingming/article/details/52892640
2、contextmanager上下文管理器
from contextlib import contextmanager
contextmanager作为上下文管理器,主要是为某些逻辑执行前做预准备工作以及某些逻辑执行后的善后工作。
contextmanager主要实现两个方法__enter__(被管理的逻辑执行前执行)和__exit__(被管理的逻辑执行后执行),可以直接自定义这两个函数,实现自定义上下文管理器。
使用contextmanager来注解某个函数后,可以通过with来调用,yeild前的内容相当于__enter__,yelid后的内容相当于__exit__;在函数中遇到yield时,在yield处停止,并把yield后的值返回。当执行完with的所有的语句后,再执行yield后面的语句
参考资料链接
3、python弱引用weakref
python的垃圾回收主要使用引用计数、标记清除、分代回收机制;python为每个对象维护一个引用计数器,当引用被创建或复制时,引用计数器+1,当对象被销毁时引用计数器-1,为0意味着没有引用,会释放内存。
标记清除:解决循环引用的问题
分代回收:通过空间换时间以提高垃圾回收效率;存活越久,扫描时间间隔越长,降低因频繁扫描导致的资源消耗
而通过调用weakref模块的ref创建对象的弱引用,不会产生引用计数,却可以操作指向的对象。
https://www.cnblogs.com/goldsunshine/p/16307598.html
threading.local()源码分析:
threading.local类使用一个_localimpl类,来作为各个线程局部变量的管理类,该类的作用用来保存各个线程局部变量,通过大字典的形式;对_localimpl的操作,实际是对局部变量大字典的操作
_localiml线程管理类的主要作用就是创建或者获取到线程局部变量字典
# { id(Thread) -> (ref(Thread), thread-local dict) }
# id(线程1):(ref(Thread), 线程1局部变量字典), ...
class _localimpl:
"""一个管理线程字典的类
# local()._local__impl = _localimpl()
# local()实例的属性_local__impl就是这个类的实例
"""
"""_local_impl只有这么多属性"""
__slots__ = 'key', 'dicts', 'localargs', 'locallock', '__weakref__'
def __init__(self):
# self.key用在线程对象中的key
self.key = '_threading_local._localimpl.' + str(id(self))
# 大字典,格式{}
# { id(Thread) -> (ref(Thread), thread-local dict) }
# id(线程1):(ref(Thread), 线程1局部变量字典), ...
self.dicts = {}
def get_dict(self):
# 从大字典self.dicts拿线程局部变量字典
thread = current_thread()
return self.dicts[id(thread)][1]
def create_dict(self):
# 为当前线程创建 线程局部变量字典
localdict = {}
key = self.key
thread = current_thread() # 当前线程
idt = id(thread) # 当前线程id
def local_deleted(_, key=key):
# 定义一个弱引用的回调函数,当线程局部变量的管理类localimpl被删除时,从线程中删除对应的变量
thread = wrthread()
if thread is not None:
del thread.__dict__[key]
def thread_deleted(_, idt=idt):
# 定义一个弱引用的回调函数,当线程被删除时,在管理类localimpl对象的字典中删除该线程的字典项
local = wrlocal()
if local is not None:
dct = local.dicts.pop(idt)
# 定义两个弱引用
wrlocal = ref(self, local_deleted)
wrthread = ref(thread, thread_deleted)
# 线程和局部变量管理类相互关联
thread.__dict__[key] = wrlocal
#在字典中以线程id为key,保存了弱引用和线程局部变量的字典
self.dicts[idt] = wrthread, localdict
# { id(Thread) -> (ref(Thread), thread-local dict) }
# id(线程1):(ref(Thread), 线程1局部变量字典), ...
return localdict
local类中通过__slots__变量定义了两个变量local__impl和__dict_,_local__impl用来保存上述局部变量管理类_localimpl,dict__则保存local类自己的成员函数或变量,可以通过self.xxx来引用到。local类中重载了__getattribute、setattr、__delattr__等函数,通过重载这些函数,将local类中的__dict__对象指向了_local__impl中保存的该线程的局部变量字典,对local类进行赋值和获取操作时,实际上是对_local__impl中保存的该线程的局部变量字典进行操作。
@contextmanagerdef _patch(self): # self是local()
# 拿到local()的属性_local__impl,_local__impl即是_localimpl()实例化的对象
impl = object.__getattribute__(self, '_local__impl')
try:
dct = impl.get_dict() # 获取线程字典局部变量字典
except KeyError:
dct = impl.create_dict() # 抛出key错误,则创建线程局部变量字典
args, kw = impl.localargs
self.__init__(*args, **kw)
with impl.locallock: # 通过上下文方式加锁 impl.locallock --> Rlock()
object.__setattr__(self, '__dict__', dct) # 给local()增加__dict__属性,指向线程小字典,即线程局部变量字典(指向字典,并非设置字典中的局部属性)
yield
class local:
# local()只有两个属性
__slots__ = '_local__impl', '__dict__'
def __new__(cls, /, *args, **kw):
if (args or kw) and (cls.__init__ is object.__init__):
raise TypeError("Initialization arguments are not supported")
self = object.__new__(cls) # 父类object创建类对象
impl = _localimpl() # _localimpl实例化
impl.localargs = (args, kw)
impl.locallock = RLock()
# 设置local()属性_local__impl,赋值为_localimpl的实例化对象
object.__setattr__(self, '_local__impl', impl)
# 调用_localimpl的create_dict()函数创建小字典(线程局部变量字典)
impl.create_dict()
return self # 返回配置好_local__impl属性的local()实例
# 重载getattribute、setattr、delattr
# getattribute、setattr、delattr操作loacl()实例,实际是操作
# _localimpl,来对线程局部变量字典的操作
def __getattribute__(self, name):
with _patch(self): # 通过上下文管理准备数据
return object.__getattribute__(self, name) # 通过上下文配置好local()的__dict__中的数据后,直接到local()的__dict__中拿
def __setattr__(self, name, value):
if name == '__dict__': # local()的__dict__只读,不能set和del
raise AttributeError(
"%r object attribute '__dict__' is read-only"
% self.__class__.__name__)
with _patch(self):
return object.__setattr__(self, name, value)
def __delattr__(self, name):
if name == '__dict__':
raise AttributeError(
"%r object attribute '__dict__' is read-only"
% self.__class__.__name__)
with _patch(self):
return object.__delattr__(self, name)