Flask中的執行緒隔離原理

2021-09-08 21:29:05 字數 2478 閱讀 6139

python因為gil(全域性直譯器鎖)的原因,本身實現的多執行緒就是偽多執行緒,同一時間只能有乙個cpu核執行乙個python應用,這裡就有人覺得python的多執行緒沒有用。這裡可以這樣理解:其實應用分為兩種,一種是cpu運算密集的應用,一種是io密集的應用,顧名思義,前者是比較依賴cpu資源,後者則是(資料庫連線請求、檔案讀取、網路請求)比較多的應用。相對於io密集的應用,python的多執行緒還是有一定的用處。

我們假設開啟了flask的多執行緒,同一時間內有多個請求進來,會生成,也就是new出多個相應的resquest物件,對應的就是檢視函式,也就是請求處理函式裡引用的request物件(from flask import request),怎麼保證在各自的執行緒裡呼叫的request就是對應的的物件?這裡有點繞。

這裡我們要介紹兩個類:local、localstack。

第三方庫werkzeug下的類werkzeug.local.local,主要是用字典dict的方式實現的該類例項物件屬性的執行緒隔離。

實現執行緒隔離的原理:local內建了乙個字典物件,每當該類的例項操作例項屬性時,會在內建字典中新增一項,key為當前執行緒的id,value為乙個字典,存的是例項屬性與值的對映,當我們要獲取這個例項的屬性時,會用當前執行緒id為key找到內建字典的對應資料,這樣就實現了執行緒隔離。我們可以看下原始碼:

class local(object):

__slots__ = ('__storage__', '__ident_func__')

def __init__(self):

# 初始化時,新建乙個字典,和乙個獲取當前執行緒id的方法get_ident

object.__setattr__(self, '__storage__', {})

object.__setattr__(self, '__ident_func__', get_ident)

# ...省略一些**

def __getattr__(self, name):

# 當我們要獲取例項的屬性值時,是根據當前執行緒的id來取

try:

return self.__storage__[self.__ident_func__()][name]

except keyerror:

raise attributeerror(name)

def __setattr__(self, name, value):

# 當我們要給例項的屬性賦值時,其實是存到內建字典裡

# key: 當前執行緒的id,value: 乙個屬性與值的對映

ident = self.__ident_func__()

storage = self.__storage__

try:

storage[ident][name] = value

except keyerror:

storage[ident] =

# ...省略一些**

我們可以用local這個類測試有沒有實現執行緒隔離,**如下:

import time

import threading

from werkzeug.local import local

temp = local()

temp.foo = 1

def do_something():

temp.foo = 2

print('new thread foo: {}'.format(temp.foo))

# 新建乙個子執行緒執行

new_thread = threading.thread(target=do_something)

new_thread.start()

# 停2秒,讓new_thread有足夠的時間執行完do_something函式

time.sleep(2)

print('main thread foo: {}'.format(temp.foo))

# 輸出:

# new thread foo: 2

# main thread foo: 1

從測試**我們可以看出,雖然我們在子執行緒中,我們把temp的屬性foo設定成了2,也讓子執行緒先執行完了,而在主線程中,temp的foo屬性還是為1,就是說這兩個執行緒對於temp物件的屬性操作是執行緒隔離的。

其實localstack是巢狀了local物件的棧,原理和local類似,如果想知道具體實現,可以自行看下原始碼,werkzeug.local.localstack。

# context locals

_request_ctx_stack = localstack()

request = localproxy(partial(_lookup_req_object, 'request'))

session = localproxy(partial(_lookup_req_object, 'session'))

由此,我們就知道了flask是怎麼實現的多個請求,request的執行緒隔離。

flask的執行緒隔離

執行緒隔離的原理就是通過字典儲存資料,每乙個執行緒都將自己的執行緒號作為key,request請求物件的返回值作為value werkzeug庫裡面的local模組封裝了乙個local物件,基於字典實現的執行緒隔離物件 import threading import time from werkze...

Python中實現執行緒隔離

import threading import time from werkzeug.local import local,localstack local是實現執行緒隔離的方法物件,localstack是封裝了實現執行緒隔離方法的棧結構物件 classa b 1my obj local my ob...

六十九 flask上下文之執行緒隔離的g物件的使用

儲存全域性物件的g物件 g物件是在整個flask應用執行期間都是可以使用的,並且也是和request一樣,是執行緒隔離的,這個物件是專門用來存放開發者自己定義的一些資料,方便在整個flask程式中都可以使用,一般使用就是將一些經常會用到的資料繫結到上面,以後就直接從g上面取就可以了,而不需要通過傳參...