flask學習筆記(十七) --上下文環境與執行緒隔離
Flask從客戶端收到請求時,要讓檢視函式能訪問一些物件,這樣才能處理請求。Flask使用請求物件封裝了客戶端傳送的HTTP請求,傳遞給檢視函式。
請求物件存在哪?請求物件存在於當前的上下文環境中。也就是說,上下文可以臨時地把某些物件變成為全域性訪問的變數。多執行緒則完成上下文環境的切換,以適應多個程序/應用“同時”執行。
一、上下文
1.概念
關於上下文,網上有很多好的帖子,比如這個 https://www.jianshu.com/p/7a7efbb7205f
相對於程序而言,上下文就是程序執行時的環境。具體來說就是各個變數和資料,包括所有的暫存器變數、程序開啟的檔案、記憶體資訊等。可以理解上下文是環境的一個快照,是一個用來儲存狀態的物件。在程式中我們所寫的函式大都不是單獨完整的,在使用一個函式完成自身功能的時候,很可能需要同其他的部分進行互動,需要其他外部環境變數的支援,上下文就是給外部環境的變數賦值,使函式能正確執行。
簡書
作者:饅頭白啊白
但我們不能把它認為是真正的全域性變數,執行緒之間必須是相互獨立的!
試想,如果你開發了一個網站。此時有多個人同時訪問你的網站,如果你把這些對伺服器的請求資訊都放在一個全域性變數request中,資料還能完整的儲存得來嗎?
這時候就需要在伺服器應用上設定一個多執行緒,完成上下文環境的切換,使得執行緒與執行緒之間互不干擾。
2.實現機制
通過棧結構來實現上下文的儲存。要知道棧是一個後入先出的儲存機制,為什麼是棧結構呢?
1. 應用上下文:Flask底層是基於werkzeug,werkzeug是可以包含多個app的,所以這時候用一個棧來儲存。如果你在使用app1,那麼app1應該是要在棧的頂部,如果用完了app1,那麼app1應該從棧中刪除。方便其他程式碼使用下面的app。
2. 如果在寫測試程式碼,或者離線指令碼的時候,我們有時候可能需要建立多個請求上下文,這時候就需要存放到一個棧中了。使用哪個請求上下文的時候,就把對應的請求上下文放到棧的頂部,用完了就要把這個請求上下文從棧中移除掉。
具體參考:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
二、執行緒&多執行緒
1.概念
也許你不理解執行緒以及多執行緒的概念,先讓我介紹一下
執行緒是程式中一個單一的順序控制流程。程序內有一個相對獨立的、可排程的執行單元,是系統獨立排程和分派CPU的基本單位指令執行時的程式的排程單位。在單個程式中同時執行多個執行緒完成不同的工作,稱為多執行緒。
百度百科
簡單理解,一個執行緒就是程式中的一條執行路徑,多執行緒就是多個不同的執行路徑(路徑與路徑之間沒有交叉)。
2.flask使用
python中有多執行緒的構造方法,利用的是Thread Local物件,實現執行緒隔離的概念。不過通過flask的Local物件能實現更加強大的功能。
#encoding: utf-8
from threading import Thread
from werkzeug.local import Local
local = Local()
local.request = '123'
class MyThread(Thread):
def run(self):
local.request = 'abc'
print('子執行緒:',local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print('主執行緒:',local.request)
輸出:
子執行緒: abc
主執行緒: 123
說明此事兩個執行緒之間已經互不影響了!!
三、回到上下文
之前講了flask實現上下文切換的方法,但是關於上下文還沒深入瞭解。現在言歸正傳,
1.在Flask中,上下文分為兩種:程式上下文、請求上下文
應用上下文和請求上下文都是存放到一個`LocalStack`的棧中。
和應用app相關的操作就必須要用到應用上下文,比如通過`current_app`獲取當前的這個`app`;
和請求相關的操作就必須用到請求上下文,比如使用`url_for`反轉檢視函式。
具體參考:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
2.兩種上下文提供的變數
current_app:程式上下文,當前啟用程式的程式例項
g: 程式上下文,處理請求時臨時儲存的物件。每次請求重設該變數。
request: 請求上下文,請求物件,封裝了客戶端發出的HTTP請求內容。
session: 使用者會話,用於儲存請求之間需要“記住”的值的字典。
3.上下文推送
在進行請求處理之前必須要推送相應的上下文,請求完成後再將其刪除,否則會報錯。
1. 在檢視函式中,不用擔心上下文的問題。因為檢視函式要執行,那麼肯定是通過訪問url的方式執行的,那麼這種情況下,Flask底層就已經自動的幫我們把請求上下文和應用上下文都推入到了相應的棧中。
2. 如果想要在檢視函式外面執行相關的操作,比如獲取當前的app(current_app),或者是反轉url,那麼就必須要手動推入相關的上下文:
* 手動推入app上下文:
# 第一種方式:
app_context = app.app_context()
app_context.push()
# 第二種方式:
with app.app_context():
print( current_app )
* 手動推入請求上下文:推入請求上下文到棧中,會首先判斷有沒有應用上下文,如果沒有那麼就會先推入應用上下文到棧中,然後再推入請求上下文到棧中:
with app.test_request_context():
print( url_for( 'my_list' ) )
四、總結
本文算是一篇入門級別的文章,更多關於上下文的資源還需自己去了解,這裡有關於flask上下文的幾篇不錯的文章:
Flask的Context(上下文)學習筆記 https://www.jianshu.com/p/7a7efbb7205f
flask上下文機制:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/