1.引入
首先,下面的代码没有采用线程隔离的方法,主线程中开启一个新线程调用test方法,最后的打印结果显然为 2
import threading
class TestThread:
value = 1
s = TestThread()
def test():
s.value = 2
new_thread = threading.Thread(target=test,name = 'test_thread')
new_thread.start()
print(s.value)
2.Local对象
(1)使用线程隔离的意义:使当前线程能够正确引用到它自己创建的对象,而不是引用到其他线程所创建的对象。
(2)在利用flask进行WEB开发中,一定有多个请求进入服务器,那如果只实例化一个request对象并指向多个请求,那就无法获得其中任何一个请求信息。因此,flask采用线程隔离栈LocalStack对象来进行线程隔离。
(3)了解LocalStack就需要先了解Local对象。简单来说,这个Local对象内部通过字典的形式,将每个线程的id作为key,请求对象信息作为value。这样,由于每个线程id号不同,自然也就可以拿到每个线程的请求信息。以下是使用Local类做的小测试:
import threading
from werkzeug.local import Local
s = Local()
s.value = 1
def test():
s.value = 2
print("新线程的value: "+str(s.value))
new_thread = threading.Thread(target=test,name = 'test_thread')
new_thread.start()
print("主线程中的value: "+str(s.value))
3.Flask中的线程隔离栈
Local使用字典的方式实现线程隔离,LocalStack封装Local对象实现了线程隔离的栈结构。这两者在使用上的区别是:使用Local对象时,可以直接像面向对象取属性一样操作,LocalStack需要进行top操作取栈顶元素(因为它毕竟是一个栈),下面是LocalStack部分源码,可以看到它内部实现了栈的一些基本操作
class LocalStack(object):
def __init__(self):
self._local = Local()
def __release_local__(self):
self._local.__release_local__()
@property
def __ident_func__(self):
return self._local.__ident_func__
@__ident_func__.setter
def __ident_func__(self, value):
object.__setattr__(self._local, "__ident_func__", value)
def __call__(self):
def _lookup():
rv = self.top
if rv is None:
raise RuntimeError("object unbound")
return rv
return LocalProxy(_lookup)
def push(self, obj):
"""Pushes a new item to the stack"""
rv = getattr(self._local, "stack", None)
if rv is None:
self._local.stack = rv = []
rv.append(obj)
return rv
def pop(self):
"""Removes the topmost item from the stack, will return the
old value or `None` if the stack was already empty.
"""
stack = getattr(self._local, "stack", None)
if stack is None:
return None
elif len(stack) == 1:
release_local(self._local)
return stack[-1]
else:
return stack.pop()
@property
def top(self):
"""The topmost item on the stack. If the stack is empty,
`None` is returned.
"""
try:
return self._local.stack[-1]
except (AttributeError, IndexError):
return None
那么也可以自己手动调用LocalStack加深印象:
from werkzeug.local import LocalStack
import threading,time
stack = LocalStack()
stack.push(1)
print("新线程push前,主线程的栈顶"+str(stack.top))
def test():
print("新线程栈顶"+str(stack.top))
stack.push(2)
print("新线程栈顶"+str(stack.top))
new_thread = threading.Thread(target=test)
new_thread.start()
time.sleep(5)
print("新线程push结束,主线程的栈顶"+str(stack.top))
新线程push前,主线程的栈顶1
新线程栈顶None
新线程栈顶2
新线程push结束,主线程的栈顶1
由此可见,每创建一个线程,该线程都会有自己的一个LocalStack来实现线程隔离
4.flask中的app和request
我们知道,flask中存在两个上下文对象(AppContext和RequestContext),flask核心对象app存放在AppContext中,请求信息Request存放在RequestContext中,那么既然Request是被线程隔离的对象(因为每次请求都需要保存当前线程的信息),app是否是被线程隔离的对象呢?
答案是否定的,核心对象app是在flask程序主入口文件创建的,也就是只有第一次请求服务器开启,会创建一个app,之后的请求都不会进入主入口文件,那么app也就不会重复创建,所以如果将app也进行线程隔离,这么做也没有太大意义。