1. 程式人生 > 實用技巧 >threading.local()使用與原理剖析

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

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() # ==== 執行結果 ==== """
小王將一封情書放在了寄存櫃裡...
小李將一雙臭襪子放在了寄存櫃裡...
三個月後,小王忽然想起了自己有件東西放在櫃子裡,決定取出來
最後他從自己的櫃子裡取出來了: 一封情書
三個月後,小李忽然想起了自己有件東西放在櫃子裡,決定取出來
最後他從自己的櫃子裡取出來了: 一雙臭襪子
"""