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也进行线程隔离,这么做也没有太大意义

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