0x01 背景知識
threading.local()
通過threading.local()
可以創(chuàng)建一個線程安全的全局變量攀芯,即在一線程中的修改不會影響另一線程盯仪。結(jié)合代碼說明:
import threading
storage = threading.local() # (1)
storage.foo = 1
print(storage.foo)
class AnotherThread(threading.Thread):
def run(self):
# 這里的 storage和定義在(1)處的并非同一個,他們只是“名字一樣”的兩個變量
storage.foo = 2
print(storage.foo)
another = AnotherThread()
another.start()
print(storage.foo)
# 由于“并非”同一個變量颠蕴,所以輸出應(yīng)該是
1
2
1
通過修改代碼驗證“名同實不同”:
import threading
storage = threading.local() # (1)
storage.foo = 1
print(storage.foo)
class AnotherThread(threading.Thread):
def run(self):
# 運行會報錯 '_thread._local' object has no attribute 'foo'
# 從錯誤信息可以看出牵啦,這個線程里面的storage和(1)處的并非同一個
print(storage.foo)
another = AnotherThread()
another.start()
print(storage.foo)
所以threading.local()
是通過創(chuàng)建“名同實不同”的“全局變量”茎活。
LocalProxy 和 LocalStack
這兩個都是基于 werkzeug.local.Local
實現(xiàn)。
werkzeug.local.Local
和threading.local
只有兩個不同辖众,一個是前者優(yōu)先使用Greenlet的ID卓起,其次是線程ID;二是前者實現(xiàn)了一個析構(gòu)方法(__release_local__
)來析構(gòu)(刪除)創(chuàng)建的werkzeug.local.Local
對象凹炸。
總的來說戏阅,前者是web升級版的threading.local
. 那么顧名思義,LocalProxy
和LocalStack
分別是線程安全的代理和線程安全的棧啤它。
0x02 上下文
關(guān)于上下文奕筐,已經(jīng)有很好的介紹文章了(見文末),我這里只做一些補充变骡。
線程不安全的情況
在單App的情況下离赫,通過current_app修改app配置會影響到其它線程。
# 在這種情況下塌碌,第二個請求時的 secret_key 的值是'new'. 其它線程顯然也會受到影響渊胸。
from flask import Flask, current_app
app = Flask(__name__)
app.secret_key = 'key'
@app.route('/')
def foo():
print(current_app.secret_key)
current_app.secret_key = 'new'
if __name__ == '__main__':
app.run(threaded=True)
```
## 為什么要有LocalProxy
按前文所述,通過``` LocalProxy ```拿到的是一個“名同實不同”的變量台妆。例如``` flask ```中的``` request ```, 他就是一個``` LocalProxy ```對象翎猛,我們經(jīng)常需要通過這個變量拿到一些請求參數(shù)。這時接剩,將其變成“名同實不同"的全局變量办成,顯然要比在每個視圖函數(shù)中都傳入一個``` request ```參數(shù)來得方便。
## 為什么要有LocalStack
按前文所述搂漠,通過``` LocalProxy ```存儲的是“名同實不同”的變量迂卢。我們已經(jīng)知道這種“名同實不同”的變量會讓我們在不同線程中方便地使用同一名稱的變量,但是如果一個線程中有多個變量呢桐汤?
> 只有棧結(jié)構(gòu)才能保存多個 Context 并在其中定位出哪個才是“當(dāng)前”而克。
在作為一個簡單的網(wǎng)站后臺時,一般不會有這種情況怔毛,因為一個HTTP請求對于一個request 環(huán)境和一個 application 環(huán)境员萍。但是,在離線測試的時候拣度,可能會推入棧中多個context碎绎。
----
相關(guān)好文
>Flask 的 Context 機(jī)制 —— Jiangge Zhang
https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
>flask 源碼解析:上下文 —— [**cizixs**](https://segmentfault.com/u/cizixs)
https://segmentfault.com/a/1190000008383197