Open csrgxtu opened 7 years ago
在操作系统的概念中,有进程和线程,这是都是为了并发的执行任务而引入的。我们知道当你在Linux系统中fork
出一个新的进程或者线程,那么这个子进程会继承使用父进程的全局变量,并且会有自己的私有变量。那么当有多个子进程的时候,读写全局变量就需要用读写锁,互斥锁,条件变量同步起来,否则最后全局变量就是脏数据了。而进程的私有变量因为只是在每个进程的内部全局可见,所以可以在进程内随便使用。
在Python中,你可以通过如下的方式access全局变量:
import threading
global_num = 0
def thread_cal():
global global_num
for i in xrange(1000):
global_num += 1
# Get 10 threads, run them and wait them all finished.
threads = []
for i in range(10):
threads.append(threading.Thread(target=thread_cal))
threads[i].start()
# wait until finished
for i in range(10):
threads[i].join()
# Value of global variable can be confused.
print global_num
如上代码,最终global_num
是多少,我们并不能准确的知道。
当然我们也可以在thread内部生命变量,然后这个变量就是当前线程全局有效的:
def show(num):
print threading.current_thread().getName(), num
def thread_cal():
local_num = 0
for _ in xrange(1000):
local_num += 1
show(local_num)
threads = []
for i in range(10):
threads.append(threading.Thread(target=thread_cal))
threads[i].start()
使用线程局部变量确实可以解决一些问题,但是在一个线程内部,往往会做很多逻辑,调用很多相关的函数,如果这些函数需要访问线程的局部变量,就需要将这些变量作为函数参数传入。
或者我们也可以将这些局部变量放入全局变量,以线程id作为key来标识,这样每个线程就可以根据自己的线程id获取到属于他的变量。
global_data = {}
def show():
cur_thread = threading.current_thread()
print cur_thread.getName(), global_data[cur_thread]
def thread_cal():
cur_thread = threading.current_thread()
global_data[cur_thread] = 0
for _ in xrange(1000):
global_data[cur_thread] += 1
show() # Need no local variable. Looks good.
...
这就是Python的lib ThreadLocal
所实现的。
所以,至此,我们理解了ThreadLocal
可以用于多线程并发。Werkzeug的Local也是如此。因为ThreadLocal
只是在多线程下的,在coroutine并发中就不适用了。其次,WSGI并没有保证处理每个请求都需要一个新的线程,而是可能会使用线程池机制,所以上一次线程执行后在ThreadLocal
中会存有脏的数据。所以Werkzeug实现了自己的Local.
LocalStack是针对Local对象实现的一种栈结构,我们可以对这个栈pop, push, top
等。因为在Werkzeug中,一个请求可能重定向,跳转,所以这里使用了Stack的结构来存储一个请求相关的数据。
# flask.py
_request_ctx_stack = LocalStack()
current_app = LocalProxy(lambda: _request_ctx_stack.top.app)
request = LocalProxy(lambda: _request_ctx_stack.top.request)
session = LocalProxy(lambda: _request_ctx_stack.top.session)
g = LocalProxy(lambda: _request_ctx_stack.top.g)
在flask.py文件中声明了全局变量_request_ctx_stack
,这个全局变量是一个Werkzeug Local类型了,所以可以在多个线程中使用而不会造成脏数据。如此,每个线程都可以全局访问属于自己线程的数据,而不会意外的修改了属于其它线程的数据,同时也不需要将request, session对象作为函数参数不停的在函数间传递了。
hummm, 以上就是Flask中currency的实现原理了,当然是借助多线程的概念。
how Flask handle concurrency?