Flask拾遺筆記之上下文

0x01 背景知識(shí)

threading.local()

通過threading.local()可以創(chuàng)建一個(gè)線程安全的全局變量,即在一線程中的修改不會(huì)影響另一線程。結(jié)合代碼說明:

import threading
storage = threading.local() # (1)
storage.foo = 1
print(storage.foo)
class AnotherThread(threading.Thread):
     def run(self):
        # 這里的 storage和定義在(1)處的并非同一個(gè),他們只是“名字一樣”的兩個(gè)變量
        storage.foo = 2
        print(storage.foo)
another = AnotherThread()
another.start()
print(storage.foo)
# 由于“并非”同一個(gè)變量,所以輸出應(yīng)該是
1
2
1

通過修改代碼驗(yàn)證“名同實(shí)不同”:

import threading
storage = threading.local() # (1)
storage.foo = 1
print(storage.foo)
class AnotherThread(threading.Thread):
     def run(self):
        # 運(yùn)行會(huì)報(bào)錯(cuò) '_thread._local' object has no attribute 'foo'
        # 從錯(cuò)誤信息可以看出,這個(gè)線程里面的storage和(1)處的并非同一個(gè)
        print(storage.foo) 
another = AnotherThread()
another.start()
print(storage.foo)

所以threading.local()是通過創(chuàng)建“名同實(shí)不同”的“全局變量”。

LocalProxy 和 LocalStack

這兩個(gè)都是基于 werkzeug.local.Local實(shí)現(xiàn)。

werkzeug.local.Localthreading.local只有兩個(gè)不同,一個(gè)是前者優(yōu)先使用Greenlet的ID,其次是線程ID;二是前者實(shí)現(xiàn)了一個(gè)析構(gòu)方法(__release_local__)來析構(gòu)(刪除)創(chuàng)建的werkzeug.local.Local對象。

總的來說,前者是web升級版的threading.local. 那么顧名思義,LocalProxyLocalStack分別是線程安全的代理和線程安全的棧。

0x02 上下文

關(guān)于上下文,已經(jīng)有很好的介紹文章了(見文末),我這里只做一些補(bǔ)充。

線程不安全的情況

在單App的情況下,通過current_app修改app配置會(huì)影響到其它線程。

# 在這種情況下,第二個(gè)請求時(shí)的 secret_key 的值是'new'. 其它線程顯然也會(huì)受到影響。

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 ```拿到的是一個(gè)“名同實(shí)不同”的變量。例如``` flask ```中的``` request ```, 他就是一個(gè)``` LocalProxy ```對象,我們經(jīng)常需要通過這個(gè)變量拿到一些請求參數(shù)。這時(shí),將其變成“名同實(shí)不同"的全局變量,顯然要比在每個(gè)視圖函數(shù)中都傳入一個(gè)``` request ```參數(shù)來得方便。

## 為什么要有LocalStack
按前文所述,通過``` LocalProxy ```存儲(chǔ)的是“名同實(shí)不同”的變量。我們已經(jīng)知道這種“名同實(shí)不同”的變量會(huì)讓我們在不同線程中方便地使用同一名稱的變量,但是如果一個(gè)線程中有多個(gè)變量呢?

> 只有棧結(jié)構(gòu)才能保存多個(gè) Context 并在其中定位出哪個(gè)才是“當(dāng)前”。

在作為一個(gè)簡單的網(wǎng)站后臺(tái)時(shí),一般不會(huì)有這種情況,因?yàn)橐粋€(gè)HTTP請求對于一個(gè)request 環(huán)境和一個(gè) application 環(huán)境。但是,在離線測試的時(shí)候,可能會(huì)推入棧中多個(gè)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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容