flask中的線程隔離

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也進行線程隔離,這麼做也沒有太大意義

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章