5.3 Flask中的线程隔离

Flask内部,通过维护一个dict来实现线程隔离。伪代码如下 request={thread_key1:Request1,thread_key2:Request2} 其中thread_key是线程的唯一id号,Request就是每次请求的Request对象

Flask内部引入了一个werkzeug的库,这个库里有一个local模块,里面有一个Local对象,Flask内部线程隔离就是通过操作Local对象实现的。

1. Local对象

Local对象实际上就是对字典原理的一个封装

class Local(object):
    __slots__ = ('__storage__', '__ident_func__')

    def __init__(self):
        # 一个私有变量__storage__字典
        object.__setattr__(self, '__storage__', {})
        object.__setattr__(self, '__ident_func__', get_ident)

    def __iter__(self):
        return iter(self.__storage__.items())

    def __call__(self, proxy):
        """Create a proxy for a name."""
        return LocalProxy(self, proxy)

    def __release_local__(self):
        self.__storage__.pop(self.__ident_func__(), None)

    def __getattr__(self, name):
        try:
            return self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

    def __setattr__(self, name, value):
        # 取当前线程的线程ID号
        ident = self.__ident_func__()
        storage = self.__storage__
        # 操作字典
        try:
            storage[ident][name] = value
        except KeyError:
            # 把线程id号作为key保存了起来
            storage[ident] = {name: value}

    def __delattr__(self, name):
        try:
            del self.__storage__[self.__ident_func__()][name]
        except KeyError:
            raise AttributeError(name)

使用线程隔离和不适用线程隔离的区别

定义一个对象,启动一个线程去修改这个对象,使用主线程打印这个对象

将my_obj实例化改为使用Local线程隔离对象

由于my_obj是一个线程隔离的对象,所以我们在新线程里修改my_obj是不会影响主线程里my_obj中的值的。他们保持了两个线程之间的数据的独立

Local的高明在于,他不需要我们去关心底层Local字典内部的细节,我们之间去操作Local对象的相关属性,这个操作本就是线程隔离的,给我们带来了很大的方便

2. 线程隔离的栈:LocalStack

image.png

接下来来继续讲解之前这张图右下角的部分。 通过Flask的源码,我们可以了解到_app_ctx_stack和_request_ctx_stack实际上是指向了LocalStack()对象,也就是一个线程隔离的栈,下面来看下源码

globals.py

LocalStack源码,依旧在werkzeug库 的local模块下

Local,Local Stack,字典的关系

Local使用字典的方式实现了线程隔离 Local Stack封装了Local对象,将其作为自己的一个属性,实现了线程隔离的栈结构

3.LocalStack的基本用法

Local是使用·来直接操作字典中保存的对象。 LocalStack是使用它提供的一些push,pop的栈方法来操作对象

4.LocalStack作为线程隔离对象的意义

image.png 从上到下,四个打印语句分别打印1,NULL,2,1;简单来说,就是两个线程会有两个栈结构,他们之间不会互相干扰。这就是LocalStack的意义

5.Flask中被线程隔离的对象

LocalStack有两个特性,一个是Local线程隔离的特性,一个是栈的特性。 Flask需要将AppContext,RequestContext做成线程隔离的,因为每次请求,在多线程环境下都是由多个线程创建。 我们想让request这一个变量名指向不同的Request对象是不可能的。 image.png

但是我们可以做到在当前线程引用到request变量名的时候可以正确的找到他自己实例化的Request对象,这就是Flask中线程隔离的本质的意义

Last updated

Was this helpful?