flask是python web开发比较主流的框架之一,也是我在工作中使用的主要开发框架。一直对其是如何保证线程安全的问题比较好奇,所以简单的探究了一番,由于只是简单查看了源码,并未深入细致研究,因此以下内容仅为个人理解,不保证正确性。
首先是很多文章都说flask会为每一个request启动一个线程,每个request都在单独线程中处理,因此保证了线程安全。于是就做了一个简单的测试。首先是写一个简单的flask程序(只需要有最简单的功能用于测试即可),然后我们知道一个flask应用启动之后实际上是作为一个 WSGI application的,之后所有接收到的请求都会经由flask的wsgi_app(self, environ, start_response)方法去处理,所以就来看一下这个方法(注释已去掉)。
defwsgi_app(self, environ, start_response):
ctx=self.request_context(environ)
ctx.push()
error=Nonetry:try:
response=self.full_dispatch_request()exceptException as e:
error=e
response=self.handle_exception(e)returnresponse(environ, start_response)finally:ifself.should_ignore_error(error):
error=None
ctx.auto_pop(error)
那么这个request_context又是什么东西呢?它是一个RequestContext对象,文档是这么说的:
The request context contains all request relevant information. It is
created at the beginning of the request and pushed to the
`_request_ctx_stack` and removed at the end of it. It will create the
URL adapter and request object for the WSGI environment provided.
说的很清楚,这个对象的上下文包含着request相关的信息。也就是说每一个请求到来之后,flask都会为它新建一个RequestContext对象,并且将这个对象push进全局变量_request_ctx_stack中,在push前还要检查_app_ctx_stack,如果_app_ctx_stack的栈顶元素不存在或是与当前的应用不一致,则首先push appcontext 到_app_ctx_stack中,再push requestcontext。源码如下:
defpush(self):
top=_request_ctx_stack.topif top is not None andtop.preserved:
top.pop(top._preserved_exc)#Before we push the request context we have to ensure that there
#is an application context.
app_ctx =_app_ctx_stack.topif app_ctx is None or app_ctx.app !=self.app:
app_ctx=self.app.app_context()
app_ctx.push()
self._implicit_app_ctx_stack.append(app_ctx)else:
self._implicit_app_ctx_stack.append(None)if hasattr(sys, 'exc_clear'):
sys.exc_clear()
_request_ctx_stack.push(self)#Open the session at the moment that the request context is
#available. This allows a custom open_session method to use the
#request context (e.g. code that access database information
#stored on `g` instead of the appcontext).
self.session =self.app.open_session(self.request)if self.session isNone:
self.session= self.app.make_null_session()
通过上面的两步,每一个请求的应用上下文和请求上下文就被push到了全局变量_request_ctx_stack和_app_ctx_stack中。
现在我们知道了_request_ctx_stack和_app_ctx_stack是何时被push的,每一个请求到来都会导致新的RequestContext和AppContext被建立并push,一旦请求处理完毕就被pop出去。而无论是_app_ctx_stack还是_request_ctx_stack都是一个LocalStack对象,这是werkzeug中的一个对象,看看它里边有什么:
classLocalStack(object):def __init__(self):
self._local=Local()def __release_local__