1.引入
首先,下面的代碼沒有采用線程隔離的方法,主線程中開啓一個新線程調用test方法,最後的打印結果顯然爲 2
import threading
class TestThread:
value = 1
s = TestThread()
def test():
s.value = 2
new_thread = threading.Thread(target=test,name = 'test_thread')
new_thread.start()
print(s.value)
2.Local對象
(1)使用線程隔離的意義:使當前線程能夠正確引用到它自己創建的對象,而不是引用到其他線程所創建的對象。
(2)在利用flask進行WEB開發中,一定有多個請求進入服務器,那如果只實例化一個request對象並指向多個請求,那就無法獲得其中任何一個請求信息。因此,flask採用線程隔離棧LocalStack對象來進行線程隔離。
(3)瞭解LocalStack就需要先了解Local對象。簡單來說,這個Local對象內部通過字典的形式,將每個線程的id作爲key,請求對象信息作爲value。這樣,由於每個線程id號不同,自然也就可以拿到每個線程的請求信息。以下是使用Local類做的小測試:
import threading
from werkzeug.local import Local
s = Local()
s.value = 1
def test():
s.value = 2
print("新線程的value: "+str(s.value))
new_thread = threading.Thread(target=test,name = 'test_thread')
new_thread.start()
print("主線程中的value: "+str(s.value))
3.Flask中的線程隔離棧
Local使用字典的方式實現線程隔離,LocalStack封裝Local對象實現了線程隔離的棧結構。這兩者在使用上的區別是:使用Local對象時,可以直接像面向對象取屬性一樣操作,LocalStack需要進行top操作取棧頂元素(因爲它畢竟是一個棧),下面是LocalStack部分源碼,可以看到它內部實現了棧的一些基本操作
class LocalStack(object):
def __init__(self):
self._local = Local()
def __release_local__(self):
self._local.__release_local__()
@property
def __ident_func__(self):
return self._local.__ident_func__
@__ident_func__.setter
def __ident_func__(self, value):
object.__setattr__(self._local, "__ident_func__", value)
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self._local, "stack", None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
那麼也可以自己手動調用LocalStack加深印象:
from werkzeug.local import LocalStack
import threading,time
stack = LocalStack()
stack.push(1)
print("新線程push前,主線程的棧頂"+str(stack.top))
def test():
print("新線程棧頂"+str(stack.top))
stack.push(2)
print("新線程棧頂"+str(stack.top))
new_thread = threading.Thread(target=test)
new_thread.start()
time.sleep(5)
print("新線程push結束,主線程的棧頂"+str(stack.top))
新線程push前,主線程的棧頂1
新線程棧頂None
新線程棧頂2
新線程push結束,主線程的棧頂1
由此可見,每創建一個線程,該線程都會有自己的一個LocalStack來實現線程隔離
4.flask中的app和request
我們知道,flask中存在兩個上下文對象(AppContext和RequestContext),flask核心對象app存放在AppContext中,請求信息Request存放在RequestContext中,那麼既然Request是被線程隔離的對象(因爲每次請求都需要保存當前線程的信息),app是否是被線程隔離的對象呢?
答案是否定的,核心對象app是在flask程序主入口文件創建的,也就是隻有第一次請求服務器開啓,會創建一個app,之後的請求都不會進入主入口文件,那麼app也就不會重複創建,所以如果將app也進行線程隔離,這麼做也沒有太大意義。