threading.local()使用與原理剖析
阿新 • • 發佈:2020-07-02
threading.local()使用與原理剖析
前言
還是第一次摘出某個方法來專門寫一篇隨筆,哈哈哈。
為什麼要寫這個方法呢?因為它確實太重要了,包括後期的Flask
框架原始碼中都有它的影子。
那麼我們就來瞄一眼這個東西是啥吧。
作用
在Python官方中文文件中(Python3.8.4),對它的介紹其實並不是很詳細
其實他的功能非常簡單,如下:
在一個全域性的容器中可以存放一些執行緒獨有的資料,這些資料應是某一執行緒私有的,是除了本執行緒外的其他執行緒訪問不到的。
舉個例子,例如你用迅雷下載的時候每條執行緒的下載進度不一樣,我們需要將下載進度這個資料儲存起來,怎麼儲存?自己建立一個具有執行緒安全性的列表或字典?那太麻煩了,請記住一點,資料怎麼存不重要,關鍵是要方便取,取的快,取的準才是王道,所以直接用
thrading.local()
方法即可。
我來畫一張吧,靈魂畫師上線,能用圖描述絕對不打字。
基本使用
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() # ==== 執行結果 ==== """ 小王將一封情書放在了寄存櫃裡... 小李將一雙臭襪子放在了寄存櫃裡... 三個月後,小王忽然想起了自己有件東西放在櫃子裡,決定取出來 最後他從自己的櫃子裡取出來了: 一封情書 三個月後,小李忽然想起了自己有件東西放在櫃子裡,決定取出來 最後他從自己的櫃子裡取出來了: 一雙臭襪子 """
&n