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

雲崖先生發表於2020-07-02

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()

# ==== 執行結果 ====

"""
小王將一封情書放在了寄存櫃裡...
小李將一雙臭襪子放在了寄存櫃裡...
三個月後,小王忽然想起了自己有件東西放在櫃子裡,決定取出來
最後他從自己的櫃子裡取出來了: 一封情書
三個月後,小李忽然想起了自己有件東西放在櫃子裡,決定取出來
最後他從自己的櫃子裡取出來了: 一雙臭襪子
"""

 

 

 

相關文章