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

接下来来继续讲解之前这张图右下角的部分。 通过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作为线程隔离对象的意义
从上到下,四个打印语句分别打印1,NULL,2,1;简单来说,就是两个线程会有两个栈结构,他们之间不会互相干扰。这就是LocalStack的意义
5.Flask中被线程隔离的对象
LocalStack有两个特性,一个是Local线程隔离的特性,一个是栈的特性。 Flask需要将AppContext,RequestContext做成线程隔离的,因为每次请求,在多线程环境下都是由多个线程创建。 我们想让request这一个变量名指向不同的Request对象是不可能的。 
但是我们可以做到在当前线程引用到request变量名的时候可以正确的找到他自己实例化的Request对象,这就是Flask中线程隔离的本质的意义
Last updated
Was this helpful?