threadiing.local和高级用法

1.threading.local介绍

threading.local()方法:给每一个线程开辟自己的空间,来存储自己的数据。在里面封装这一些对应此线程
注意:
  虽然flask没有threading.local(),但是flask内部实现了一个和threading.local()类似的功能~

 代码实现

复制代码
from threading import local
import threading
import time

class Foo(object):
    def __init__(self):
        self.num = 0
# 当每个线程在执行 val1.xx=1 ,在内部会为此线程开辟一个空间,来存储 xx=1
# val1.xx,找到此线程自己的内存地址去取自己存储 xx,根据线程的id来进行区分线程。
val1 = local()
val = Foo()
def func(item):
    val1 = item
    val.num = val1
    time.sleep(3)
    # print(val.num)
    print(val1)

if __name__ == '__main__':
    for item in range(5):
        t = threading.Thread(target=func,args=(item,))
        t.start()
复制代码

2.为什么要写threading.local的用法,和flask有什么关系嘛

这是因为在flask中有个local类,他和threading.local的功能一样,为每个线程开辟空间进行存取数据,他们两个的内部实现机制一样,内部维护一个字典,以线程(协程)ID为key,进行数据隔离。(个人觉得加锁可以,但是效率就会降低)

3.通过代码来实现一个threading.local的功能

在了解flask里面的local类功能之前,可以通过栈,面相对象,和线程的知识爱实现一个自己写的threading.loca()的功能

知识点一:栈

  栈:栈是具有后进先出的特性,列子是弹夹,可以使用列表的append和pop方法将列表维护成一个栈
  应用场景:
    drf源码中的节流组件

 知识点二:面向对象相关

复制代码
class Foo(object):
    def __setattr__(self, key, value):
        print(key,value)

    def __getattr__(self, item):
        item =456
        return item
# obj = Foo()    # 实例化执行的是__init__方法
# obj.x =123     # 在obj.x 后面加=是代表赋值,会自动的执行__setattr__方法。其中key=x,value=123
# print(obj.x)   # 在obj.x 后面不加=的时候,是获取值,执行__getattr__方法,其中x=456,是__getattr__的返回值
# 应用:
    # 在drf的request里面,新的request.data和request.quert_parmas是新封装的request对象
    # 但是新的request依然可以使用request.POST,是因为在新的erequest里面有一个__getattr__
    # 方法,一旦自己的类里面没有该方法,就会执行老的request里面的POST方法
复制代码

 __attr__的应用(小列子)

复制代码
# 看如下代码:会返回什么?
class
Local(object): def __init__(self): self.storage = {} def __setattr__(self, key, value): self.storage[key] = value def __getattr__(self, item): return self.storage.get(item) local = Local() # local = Local(),执行了__init__方法里面的self.storage = {} # 其中self.storage = {}相当于是赋值操作,所以会调用自己的__setattr__方法 # 但是其中的self.strorage(后面的不是)是来获取值了(整句的意思是将vale赋值给storage,但是这里面没有,必须的先获取在赋值),就会去执行__getattr__方法 # 但是__getattr__连的self.storage又去获取值去了,又去执行__getattr__方法,造成递归,所以程序报错 # 所以我们可以换一中写法,在__init__继承父类的__setattr__方法 class Local(object): def __init__(self): object.__setattr__(self,"storage",{}) # 这种赋值是在给父类的__setattr__对象里面创建了一个 storage = {} 的字典 # 实力化对象在执行__init__的时候会在storage = { 'key':{1:"123"}}字典里面赋值 # 也可以防止上面的递归发生 def __setattr__(self, key, value): self.storage[key] = value def __getattr__(self, item): return self.storage.get(item) local = Local() local.key = {1:"123"} # 可以设置值了 print(local.x) # 可以获取值了,但是为空,因为没设置 print(local.storage) # 也可以获取storage字典 print(local.key) # 也可以获取key字典 print(local.__dict__) # # __dict__是将对象里面的字典类型转成字典
复制代码

知识点三:线程唯一id

复制代码
import threading
from threading import get_ident   # 通过模块获取线程的唯一标识,可以理解为线程的id

def task():
    ident = get_ident()
    print(ident)

for i in range(4):
    t = threading.Thread(target=task)
    t.start()
复制代码

4.自定义:普通版实现threading.local函数的功能

复制代码
import threading
class Local(object):
    def __init__(self):
        # 调用父类的__setattr__方法,相当于在父类里面创建self.storage= {}
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        ident = threading.get_ident()  # 获取当前线程唯一标识
        if ident in self.storage:
            self.storage[ident][key] = value  # 如果ident在storage里面,就将key:value赋值给ident的数组
            # {'storage': {98520: {'x1': 0}}}
            #  storage     ident   key  value
        else:
            self.storage[ident] = {key:value}

    def __getattr__(self, item):
        ident = threading.get_ident()
        if ident not in self.storage:
            return
        return self.storage[ident].get(item)
        # self.storag[ident] 获取父类的self.storag[ident],因为Local里面没有self.storag字典
local = Local()  # 执行父类的__setattr__

def task(arg):
    local.x1 = arg  # 执行Local的__setattr__
    print(local.x1)
    # print(local.__dict__)
    #  {
    #       'storage':
    #           {
    #           98520: {'x1': 0},
    #           100180: {'x1': 1},
    #           99508: {'x1': 2},
    #           72148: {'x1': 3},
    #           101740: {'x1': 4}
    #           }
    # }

for i in range(5):
    t = threading.Thread(target=task,args=(i,))
    t.start()

# 自定义的方法完全实现了treading.local方法相同的功能
复制代码

5.自定义:高级版实现threading.local的功能

复制代码
import threading
class Local(object):
    def __init__(self):
        object.__setattr__(self,'storage',{})

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in self.storage:
            self.storage[ident][key].append(value)  # 使用列表维护成一个栈
        else:
            self.storage[ident] = {key:[value,]}

    def __getattr__(self, item):
        ident = threading.get_ident()
        if ident not in self.storage:
            return
        return self.storage[ident][item][-1]  # 取列表的最后一个值

local = Local()

def task(arg):
    local.x1 = arg
    print(local.x1)
    # print(local.__dict__)  等价于storage的字典
    #{
    # 'storage':
    #       {
    #           83880: {'x1': [0]},
    #           101040: {'x1': [1]},
    #           99688: {'x1': [2]},
    #           94888: {'x1': [3]}, 
    #           99760: {'x1': [4]}
    #       }
    # }
for i in range(5):
    t = threading.Thread(target=task,args=(i,))
    t.start()

# 在字典的内部维护一个列表,使用列表的append和pop来完成栈的功能
# 取值的时候storage[ident][item][-1] 获取栈顶的值。
# 高级版的比普通版的只是将valve存成了列表
复制代码

总结

这也是flask实现threading.local功能的简单版代码实现
那么我们为什么要费劲的了解threading.local以及自己实现threading.local的功能那???
  因为这是flask的上下文管理的基本,也是实现部分

请看下一篇文章:flask中Localstack和Local对象实现

 

pass

posted @   thep0st  阅读(99)  评论(0编辑  收藏  举报
(评论功能已被禁用)
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示