Flask从客户端收到请求时,要让视图函数能访问一些对象,这样才能处理请求。Flask使用请求对象封装了客户端发送的HTTP请求,传递给视图函数。
请求对象存在哪?请求对象存在于当前的上下文环境中。也就是说,上下文可以临时地把某些对象变成为全局访问的变量。多线程则完成上下文环境的切换,以适应多个进程/应用“同时”运行。
一、上下文
1.概念
关于上下文,网上有很多好的帖子,比如这个 https://www.jianshu.com/p/7a7efbb7205f
相对于进程而言,上下文就是进程执行时的环境。具体来说就是各个变量和数据,包括所有的寄存器变量、进程打开的文件、内存信息等。可以理解上下文是环境的一个快照,是一个用来保存状态的对象。在程序中我们所写的函数大都不是单独完整的,在使用一个函数完成自身功能的时候,很可能需要同其他的部分进行交互,需要其他外部环境变量的支持,上下文就是给外部环境的变量赋值,使函数能正确运行。
简书
作者:馒头白啊白
但我们不能把它认为是真正的全局变量,线程之间必须是相互独立的!
试想,如果你开发了一个网站。此时有多个人同时访问你的网站,如果你把这些对服务器的请求信息都放在一个全局变量request中,数据还能完整的存储得来吗?
这时候就需要在服务器应用上设置一个多线程,完成上下文环境的切换,使得线程与线程之间互不干扰。
2.实现机制
通过栈结构来实现上下文的保存。要知道栈是一个后入先出的存储机制,为什么是栈结构呢?
1. 应用上下文:Flask底层是基于werkzeug,werkzeug是可以包含多个app的,所以这时候用一个栈来保存。如果你在使用app1,那么app1应该是要在栈的顶部,如果用完了app1,那么app1应该从栈中删除。方便其他代码使用下面的app。
2. 如果在写测试代码,或者离线脚本的时候,我们有时候可能需要创建多个请求上下文,这时候就需要存放到一个栈中了。使用哪个请求上下文的时候,就把对应的请求上下文放到栈的顶部,用完了就要把这个请求上下文从栈中移除掉。
具体参考:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
二、线程&多线程
1.概念
也许你不理解线程以及多线程的概念,先让我介绍一下
线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
百度百科
简单理解,一个线程就是程序中的一条执行路径,多线程就是多个不同的执行路径(路径与路径之间没有交叉)。
2.flask使用
python中有多线程的构造方法,利用的是Thread Local对象,实现线程隔离的概念。不过通过flask的Local对象能实现更加强大的功能。
#encoding: utf-8
from threading import Thread
from werkzeug.local import Local
local = Local()
local.request = '123'
class MyThread(Thread):
def run(self):
local.request = 'abc'
print('子线程:',local.request)
mythread = MyThread()
mythread.start()
mythread.join()
print('主线程:',local.request)
输出:
子线程: abc
主线程: 123
说明此事两个线程之间已经互不影响了!!
三、回到上下文
之前讲了flask实现上下文切换的方法,但是关于上下文还没深入了解。现在言归正传,
1.在Flask中,上下文分为两种:程序上下文、请求上下文
应用上下文和请求上下文都是存放到一个`LocalStack`的栈中。
和应用app相关的操作就必须要用到应用上下文,比如通过`current_app`获取当前的这个`app`;
和请求相关的操作就必须用到请求上下文,比如使用`url_for`反转视图函数。
具体参考:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/
2.两种上下文提供的变量
current_app:程序上下文,当前激活程序的程序实例
g: 程序上下文,处理请求时临时存储的对象。每次请求重设该变量。
request: 请求上下文,请求对象,封装了客户端发出的HTTP请求内容。
session: 用户会话,用于存储请求之间需要“记住”的值的字典。
3.上下文推送
在进行请求处理之前必须要推送相应的上下文,请求完成后再将其删除,否则会报错。
1. 在视图函数中,不用担心上下文的问题。因为视图函数要执行,那么肯定是通过访问url的方式执行的,那么这种情况下,Flask底层就已经自动的帮我们把请求上下文和应用上下文都推入到了相应的栈中。
2. 如果想要在视图函数外面执行相关的操作,比如获取当前的app(current_app),或者是反转url,那么就必须要手动推入相关的上下文:
* 手动推入app上下文:
# 第一种方式:
app_context = app.app_context()
app_context.push()
# 第二种方式:
with app.app_context():
print( current_app )
* 手动推入请求上下文:推入请求上下文到栈中,会首先判断有没有应用上下文,如果没有那么就会先推入应用上下文到栈中,然后再推入请求上下文到栈中:
with app.test_request_context():
print( url_for( 'my_list' ) )
四、总结
本文算是一篇入门级别的文章,更多关于上下文的资源还需自己去了解,这里有关于flask上下文的几篇不错的文章:
Flask的Context(上下文)学习笔记 https://www.jianshu.com/p/7a7efbb7205f
flask上下文机制:https://blog.tonyseek.com/post/the-context-mechanism-of-flask/