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

前言

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

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

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

作用

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

image-20200701215931356

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

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

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

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

image-20200701221438544

基本使用

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

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

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

import threading
 time

def operate(name):
    """操作"""
    print("三个月后,{0}忽然想起了自己有件东西放在柜子里,决定取出来".format(name))
    最后他从自己的柜子里取出来了:,Locker.article)



 people(article):
    name = threading.current_thread().getName()  # 获取线程名
    Locker.article = article    这就表示将私有的的一件物品放在了寄存柜里
    {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,1)">一双臭袜子小李)
    t1.start()
    t2.start()
    t1.join()
    t2.join()

 ==== 执行结果 ====


小王将一封情书放在了寄存柜里...
小李将一双臭袜子放在了寄存柜里...
三个月后,小王忽然想起了自己有件东西放在柜子里,决定取出来
最后他从自己的柜子里取出来了: 一封情书
三个月后,小李忽然想起了自己有件东西放在柜子里,决定取出来
最后他从自己的柜子里取出来了: 一双臭袜子
"""

原理分析

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

 time

Locker = {}   好了寄存柜放在全局了

{
线程id:{"article":"存放的具体物品"},
线程id:{"article":"存放的具体物品"},
线程id:{"article":"存放的具体物品"},
}
"""

.format(name))
    ident = threading.get_ident()   获取线程ID
    article])



 获取线程名
    ident = threading.get_ident()   获取线程ID
    Locker[ident] = {}   创建了一个寄存柜的小格子
    Locker[ident]["] = article   这就表示将私有的的一件物品放在了寄存柜里
    :

    t1 = threading.Thread(target=people,小李忽然想起了自己有件东西放在柜子里,决定取出来
最后他从自己的柜子里取出来了: 一双臭袜子
三个月后,小王忽然想起了自己有件东西放在柜子里,决定取出来
最后他从自己的柜子里取出来了: 一封情书
"""

尝试自己做出一个寄存柜

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

 time


class MyLocker(object):
    cabinet = {}   柜子
    
    {
    线程id:{"article":"存放的具体物品"},
    线程id:{"article":"存放的具体物品"},
    线程id:{"article":"存放的具体物品"},
    }
    """

    def __getattr__(self,item):
        当访问属性不存在时触发"""
        ident = threading.get_ident()
        return MyLocker.cabinet[ident][item]

    __setattr__试图用 . 去设置属性时触发if ident in MyLocker.cabinet:   如果在柜子里这重新赋值
            MyLocker.cabinet[ident][key] = value
        else:
            MyLocker.cabinet[ident] = {key: value}   如果不在则做一个小格子,并且把物件存放进来


:
    Locker = MyLocker()  """

 

相关文章

功能概要:(目前已实现功能)公共展示部分:1.网站首页展示...
大体上把Python中的数据类型分为如下几类: Number(数字) ...
开发之前第一步,就是构造整个的项目结构。这就好比作一幅画...
源码编译方式安装Apache首先下载Apache源码压缩包,地址为ht...
前面说完了此项目的创建及数据模型设计的过程。如果未看过,...
python中常用的写爬虫的库有urllib2、requests,对于大多数比...