python 多线程 ThreadLocal 原理解析
1、有什么用ThreadLocal
在多线程环境下,使用thread.local 对象 可以为每个线程创建单独自己的数据(相当于给每个线程创建里属于每个线程的局部变量),而不用考虑多线程时使用全局变量 需要加锁的问题。
2、代码解析
import threading,time ''' threading.local()方法 原理 1 初步: local 内部 通过字典维护 仅属于当前线程的变量 注意:字典是线程安全的 ''' local_t = threading.local() # 定义一个全局字典变量,用于存储不同线程的 局部变量 thread_dict = {} class Local: # 使类的实例具有 obj.key=value的能力 def __setattr__(self, key, value): # 获得线程的id thread_id = threading.get_ident() # 判断线程id是否存在于全局变量dict中 # 不存在 则将线程id作为外层dict的key,传入的值作为内层dict key,value 则作为内层dict 的key if thread_id in thread_dict: thread_dict[thread_id][key]=value # 存在为内层dict 增加一对key:value else: thread_dict[thread_id]={key:value} # print(thread_dict) # 使类的实例具有 obj.key 可以直接获取示例变量值的能力 def __getattr__(self, item): thread_id = threading.get_ident() # 通过实例.变量的方式取值时,永远取 内层字典的键值对的值 return thread_dict[thread_id][item] '''假设 obj1 = Local() obj1.val = 1 obj1.val2=2 print(obj1.val) Local 类的全局变量 thread_dict= { 447372: {'val': 1, 'val2': 2} } 通过上述类 创建的过程解析 ''' obj = Local() def func(arg): obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1) # time.sleep(0.01) print(obj.phone,arg) for i in range(10): t = threading.Thread(target=func,args=(i,)) t.start()
3、threading.local()本质解析:
local类 定义一个全局的字典变量 (注意 字典 是线程安全的)
字典数据结构:
{ 'threadid1': { 'key1': 1, 'key2': 2 }, 'threadid2': { 'key2': 1, 'key4': 2 } }
两层字典的结构:
外层字典:threadId 作为key,local的实例变量和值作为value,
里层字典:local的实例变量作为key,值作为value
类的内部重写
__setattr__
通过将线程id作为外层变量key ,线程变量和值按格式塞进字典中
__getattr__
取值的时候 通过线程id 取出对应线程里面存储的数据,在通过变量作为key取之前线程绑定的局部变量的值