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取之前线程绑定的局部变量的值



posted @ 2021-11-18 15:10  昆虫白  阅读(680)  评论(0)    收藏  举报