线程隔离思想
原理是使用字典来保存数据
当然我们还要操作数据,我们把线程隔离的数据封装到一个对象,这样我们在外部直接使用该对象的相关方法就可以了
flask中引用werkzeug库,里面有个local模块,这个模块下面有个Local对象, flask做线程隔离的实质就是由Local对象完成的,Local对象的本质就是字典的方式实现线程隔离的,是对线程id字典的一个封装
Local是一个线程隔离的对象,相关属性的操作不同线程之间是互相不影响的,保存了线程之间的独立,
import threading
from werkzeug.local import Local
my_obj = Local()
my_obj.b = 1
def worker():
# 新线程
my_obj.b = 2
print('in new thread b is:' + str(my_obj.b))
new_t = threading.Thread(target=worker, name='new-thread')
new_t.start()
print('in main thread b is:', my_obj.b)
执行结果:
in new thread b is:2
in main thread b is: 1
线程隔离的栈
- image.png
LocalStack()是一个线程隔离的对象,可以用来做线程隔离的栈,使用Local()对象实现,LocalStack和Local都是可以单独使用的额,不需要依赖于flask,都是单独的库,和flask之间没有直接的关系
LocalStack,Local,字典之间的关系
Local使用字典的方式实现的线程隔离,而LocalStack封装了Local对象,把Local对象作为它的一个属性,从而实现了一个线程隔离的栈结构
封装,如果一次封装解决不了问题,就再来一次
编程也是一种艺术,编码要含蓄
LocalStack的使用
- Local可以当做一个普通对象,通过点来操作,而LocalStack必须要使用它定义的push,pop,top来操作
# -*- coding: utf-8 -*-
from werkzeug.local import LocalStack
s = LocalStack()
s.push(1)
print(s.top)
print(s.top)
print(s.pop()) # pop()删除最后入栈的一个元素,并返回
print(s.top)
s.push(1)
s.push(2)
print(s.top)
print(s.top)
print(s.pop())
print(s.top)
执行结果:
1
1
1
None
2
2
2
1
- 栈,后进先出,只能取栈顶元素,数据结构就是限制了某些能力
# -*- coding: utf-8 -*-
import threading
from werkzeug.local import LocalStack
my_stack = LocalStack()
my_stack.push(1)
print('in main thread after push ,value is ' + str(my_stack.top))
def worker():
# 新线程
print('in new thread before push ,value is ' + str(my_stack.top))
my_stack.push(2)
print('in new thread before push ,value is ' + str(my_stack.top))
new_t = threading.Thread(target=worker, name='new-thread')
new_t.start()
print('finally, in main thread ,value is ' + str(my_stack.top))
执行结果:
in main thread after push ,value is 1
in new thread before push ,value is None
in new thread before push ,value is 2
finally, in main thread ,value is 1
flask中的LocalStack
- image.png
用来隔离应用上下文,请求上下文, 在多线程中,每个线程都会创建对象,如果不隔离的话,很容易发生混淆,容易造成程序错误
flask中被线程隔离的对象,Request,request,g, session
- image.png
线程隔离对象
线程隔离对象:Local,LocalStack
被线程隔离的对象: AppContext, RequestContext
Request是位于RequestContext的内部,所以它是一个间接是实现的线程隔离
AppContext和flask核心对象app是两个对象,一定不要混淆,flask的核心对象app作为一个属性存在于AppContext下
flask的核心对象在全局中只有一个,current_app便是flask的核心对象app,全局只有一个,所以对于current_app来说,线程隔离是没有意义的
如果所有的用户,所有的请求都共享同一份数据,可以考虑将数据爆保存到app上来,比如网站的访问计数,当然,这个存在线程安全的问题
- image.png