+ 我要发布
我发布的 我的标签 发现
浏览器扩展
斑点象@Edge

了解Flask中的临时全局变量

Flask为什么会有临时全局变量?为了避免处理HTTP请求的函数传入参数过多,Flask发明了临时全局变量,让每个处理HTTP请求的函数都能更加优雅。 首先了解下Flask是如何处理HTTP请求的。 当Application服务器接收到HTTP请求时,它会将请求转发给flask.py中的Flask类进行处理。Flask类会如何处理这些请求呢?先来了解一下什么是服务器I/O模型。 一、服务器I/O模型 1:阻塞式单线程:这是最简单的I/O模型,服务器只有在处理完一个请求之后才会处理下一个请求。这种模型的缺点举个例子,超时结账时走人工通道,需要排队依次结账,排队是需要等待的。 2:阻塞式多线程:这是一种改进的阻塞式单线程模型,服务器为每个请求开一个线程进行处理。当线程数量达到一定数量时,线程之间的切换和上下文操作会消耗大量CPU时间,造成资源的浪费。 3:非阻塞式事件驱动:这种模型需要提到反应器设计模式(Rector Pattern),它是一种事件驱动的设计模式,用于处理多个请求同时发送到服务器的情况。该模型的优点在于降低了CPU资源消耗,缺点在于难以用程序编写。 4:协程:下面单独介绍它。 二、协程 1:什么是协程? 协程是比线程更小的执行单元,自带CPU上下文。当我们从一个协程切换到另一个协程时,通过保存/恢复CPU上下文,使程序可以继续运行。 2:协程和线程的区别是什么? 协程切换只需要保存和恢复CPU上下文,而线程除了保存和恢复CPU上下文外,还需要进行线程缓存的保存与恢复。线程的切换会消耗大量的CPU性能,而协程可以在一秒钟内切换上百万次而不会有什么压力。 3:为什么要进行线程/协程的切换? 当CPU执行所谓的并行处理时,它会将CPU时间分成多个部分,先执行一个线程的任务,再执行另一个线程的任务,然后再回头执行第一个线程的任务。由于CPU切换非常快,我们感觉两个线程同时运行。但是这样做不可避免地涉及到大量的CPU切换,缩小切换成本成为处理并发问题的关键。 4:1:N模式 1:N模式是指将一个线程作为容器来放置多个协程。 5:协程的切换 在协程中,自己是主动让出CPU的。每个协程池都有一个调度器,它是一个被动调度器,不会主动切换协程。相反,当一个协程发现无法继续执行时,它会主动联系调度器,然后调度器根据调度算法找到当前最需要CPU的协程。 当一个协程需要进行大量的CPU计算(即它是CPU密集型的),并且该协程不涉及I/O时,就可能会出现该协程一直占据着CPU的情况。 这种情况需要程序员手动处理。 三、Python处理并发的两个库:Greenlet和Stackless Greenlet使用的服务器I/O模型就是协程。 Stackless提供了对协程的原生支持,可以轻松地创建、管理和调度大量协程。 四、Flask中的Thread Local概念是理解临时全局变量的关键 从面向对象设计的角度来看,对象是保存“状态”的地方。Python也是如此,一个对象的状态都被保存在对象携带的特殊字典中,可以通过vars拿到它。 Thread Local是一种特殊的对象,它的“状态”对线程(协程)隔离,也就是说对于每一个线程(协程)来说,Thread Local的值都不一样(例如:变量a是个Thread Local对象,初始化的值为0,线程1中修改a=1,在线程2中看到的a的值却还是0——每个线程对一个Thread Local对象的修改都不会影响其他线程)。 Thread Local是如何实现的呢? 它以线程的ID(协程的ID——由Greenlet提供)保存多个状态词典,每个线程(协程)访问它时就可以去找自己ID对应的状态来实现Thread Local提供的这种奇妙特性了。 只要能构造出Thread Local对象,就能够让同一个对象在多个线程(协程)下做到状态隔离,Flask中的临时全局变量就可以实现了。 五、Python中获得Thread Local的两种方法 方法1: 使用Python自带的threading.local线程局部变量。 只支持在线程之间作为局部变量。 方法2: 使用Werkzeug实现的werkzeug.local.Local类,它的优势在于支持在协程之间作为局部变量。它会优先使用Greenlet的ID而不是现成的ID来保存状态词典。它也支持基于greenlet的面向网络应用的并发处理框架——eventlet,以及基于协程的Python网络函数库——gevent。 六、Werkzeug实现的两种数据结构:LocalStack和LocalProxy LocalStack使用werkzeug.local.Local类实现的栈结构。 LocalProxy是一个代理模式实现,具体实现不再说明,其返回值是Thread Local。 七、Flask基于LocalStack和LocalProxy实现的Context和临时全局变量 Flask是一个基于Werkzeug实现的框架,Flask的App Context(程序上下文)和Request Context(请求上下文)也基于Werkzeug的Local Stack实现。current_app、g、request、session实际上都是LocalProxy类型的对象(本质上是Thread Local对象),它们都具有临时全局变量的功能。
我的笔记