zoukankan      html  css  js  c++  java
  • threading.local()使用与原理剖析

    threading.local()使用与原理剖析

    前言

      还是第一次摘出某个方法来专门写一篇随笔,哈哈哈。

      为什么要写这个方法呢?因为它确实太重要了,包括后期的Flask框架源码中都有它的影子。

      那么我们就来瞄一眼这个东西是啥吧。

     

    作用

      在Python官方中文文档中(Python3.8.4),对它的介绍其实并不是很详细

     

    image-20200701215931356

      其实他的功能非常简单,如下:

     

      在一个全局的容器中可以存放一些线程独有的数据,这些数据应是某一线程私有的,是除了本线程外的其他线程访问不到的。

     

      举个例子,例如你用迅雷下载的时候每条线程的下载进度不一样,我们需要将下载进度这个数据存储起来,怎么存储?自己创建一个具有线程安全性的列表或字典?那太麻烦了,请记住一点,数据怎么存不重要,关键是要方便取,取的快,取的准才是王道,所以直接用thrading.local()方法即可。

     

      我来画一张吧,灵魂画师上线,能用图描述绝对不打字。

    image-20200701221438544

     

    基本使用

     

      threading.local():可以实例化出一个寄存柜,这个寄存柜是全局化的

      寄存柜对象.你想放的东西名字 = 东西:你可以在线程中这样放入一个独有的物件

      寄存柜对象.你放过的东西的名字:这样,你就可以将你存放进的东西拿出来。注意,只有你才能拿出来,其他人拿不了。

     

    import threading
    import time
    
    def operate(name):
        """操作"""
        print("三个月后,{0}忽然想起了自己有件东西放在柜子里,决定取出来".format(name))
        print("最后他从自己的柜子里取出来了:",Locker.article)
    
    
    
    def people(article):
        name = threading.current_thread().getName()  # 获取线程名
        Locker.article = article  #  这就表示将私有的的一件物品放在了寄存柜里
        print("{0}将{1}放在了寄存柜里...".format(name,article))
        time.sleep(3)  # 过了三个月,这期间发生了很多事
        operate(name)
    
    
    if __name__ == '__main__':
        Locker = threading.local()  # 好了寄存柜放在全局了
        t1 = threading.Thread(target=people,args=("一封情书",),name="小王")
        t2 = threading.Thread(target=people,args=("一双臭袜子",),name="小李")
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
    # ==== 执行结果 ====
    
    """
    小王将一封情书放在了寄存柜里...
    小李将一双臭袜子放在了寄存柜里...
    三个月后,小王忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一封情书
    三个月后,小李忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一双臭袜子
    """

     

    原理分析

      我们可以自己做一个全局字典,来实现与这个类似的功能,但是使用起来肯定不太方便(它实际上本质就是字典嵌套进行存储的):

    import threading
    import time
    
    Locker = {}  # 好了寄存柜放在全局了
    """
    {
    线程id:{"article":"存放的具体物品"},
    线程id:{"article":"存放的具体物品"},
    线程id:{"article":"存放的具体物品"},
    }
    """
    
    def operate(name):
        """操作"""
        print("三个月后,{0}忽然想起了自己有件东西放在柜子里,决定取出来".format(name))
        ident = threading.get_ident()  # 获取线程ID
        print("最后他从自己的柜子里取出来了:",Locker[ident]["article"])
    
    
    
    def people(article):
        name = threading.current_thread().getName()  # 获取线程名
        ident = threading.get_ident()  # 获取线程ID
        Locker[ident] = {}  # 创建了一个寄存柜的小格子
        Locker[ident]["article"] = article  # 这就表示将私有的的一件物品放在了寄存柜里
        print("{0}将{1}放在了寄存柜里...".format(name,article))
        time.sleep(3)  # 过了三个月,这期间发生了很多事
        operate(name)
    
    
    if __name__ == '__main__':
    
        t1 = threading.Thread(target=people,args=("一封情书",),name="小王")
        t2 = threading.Thread(target=people,args=("一双臭袜子",),name="小李")
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
    # ==== 执行结果 ====
    
    """
    小王将一封情书放在了寄存柜里...
    小李将一双臭袜子放在了寄存柜里...
    三个月后,小李忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一双臭袜子
    三个月后,小王忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一封情书
    """

     

    尝试自己做出一个寄存柜

      这样做是不是太麻烦了?我们可以自定义一个类,让它变得更加简单,如同原本的使用方法一样。其实下面代码中采取的方法也是threading.local()所采取的方法。

    import threading
    import time
    
    
    class MyLocker(object):
        cabinet = {}  # 柜子
        """
        {
        线程id:{"article":"存放的具体物品"},
        线程id:{"article":"存放的具体物品"},
        线程id:{"article":"存放的具体物品"},
        }
        """
    
        def __getattr__(self, item):
            """当访问属性不存在时触发"""
            ident = threading.get_ident()
            return MyLocker.cabinet[ident][item]
    
        def __setattr__(self, key, value):
            """试图用 . 去设置属性时触发"""
            ident = threading.get_ident()
            if ident in MyLocker.cabinet:  # 如果在柜子里这重新赋值
                MyLocker.cabinet[ident][key] = value
            else:
                MyLocker.cabinet[ident] = {key: value}  # 如果不在则做一个小格子,并且把物件存放进来
    
    
    def operate(name):
        """操作"""
        print("三个月后,{0}忽然想起了自己有件东西放在柜子里,决定取出来".format(name))
        print("最后他从自己的柜子里取出来了:", Locker.article)
    
    
    def people(article):
        name = threading.current_thread().getName()  # 获取线程名
        Locker.article = article  # 这就表示将私有的的一件物品放在了寄存柜里
        print("{0}将{1}放在了寄存柜里...".format(name, article))
        time.sleep(3)  # 过了三个月,这期间发生了很多事
        operate(name)
    
    
    if __name__ == '__main__':
        Locker = MyLocker()  # 好了寄存柜放在全局了
        t1 = threading.Thread(target=people, args=("一封情书",), name="小王")
        t2 = threading.Thread(target=people, args=("一双臭袜子",), name="小李")
        t1.start()
        t2.start()
        t1.join()
        t2.join()
    
    # ==== 执行结果 ====
    
    """
    小王将一封情书放在了寄存柜里...
    小李将一双臭袜子放在了寄存柜里...
    三个月后,小王忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一封情书
    三个月后,小李忽然想起了自己有件东西放在柜子里,决定取出来
    最后他从自己的柜子里取出来了: 一双臭袜子
    """

     

     

  • 相关阅读:
    JavaScript界面传值与前后台互调
    @requestBody注解的使用
    mybatis动态sql中foreach标签的使用
    mybatis动态SQL中的sql片段
    mybatis动态sql中where标签的使用
    mybatis动态sql中的trim标签的使用
    mybatis动态SQL中的set标签的使用
    SQL-join(inner join)、left join、right join、full join
    Oracle-distinct()用法、count(distinct( 字段A || 字段B))是什么意思?distinct多个字段
    Oracle-where exists()、not exists() 、in()、not in()用法以及效率差异
  • 原文地址:https://www.cnblogs.com/Yunya-Cnblogs/p/13222482.html
Copyright © 2011-2022 走看看