1
多个线程操作同一对象带来的问题
多个线程修改同一对象属性,会造成数据错乱。
import time
import threading
class A:
b = 1
obj1 = A()
def worker():
obj1.b = 2
t1 = threading.Thread(target=worker)
t1.start()
time.sleep(1)
print(obj1.b)
输出结果:
2
2
如何做到线程隔离?
"""
Local是被线程隔离的对象
"""
import time
import threading
from werkzeug.local import Local
obj1 = Local()
obj1.b = 1
def worker():
obj1.b = 2
print("子线程中obj1.b的值为{}".format(obj1.b))
t1 = threading.Thread(target=worker)
t1.start()
time.sleep(1)
print("主线程中obj1.b的值为{}".format(obj1.b))
输出结果:
子线程中obj1.b的值为2
主线程中obj1.b的值为1
所以,凡是继承了Local的,都是线程隔离的
3
Local实现线程隔离的方法
阅读源码文件,local.py
def __setattr__(self, name, value):
ident = self.__ident_func__() # 取当前线程的线程ID号
storage = self.__storage__
# 操作字典,把线程id号作为key保存起来
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
原来,Local是通过字典这种python中的数据结构实现的线程隔离。每一个线程都有对应的自己的线程ID, 并将对象作为value存放。如:{线程ID1:对象, 线程ID2:对象}
4
flask的线程隔离栈 LocalStack
阅读flask源码文件globals.py文件
# context locals
_request_ctx_stack = LocalStack()
_app_ctx_stack = LocalStack()
current_app = LocalProxy(_find_app)
request = LocalProxy(partial(_lookup_req_object, 'request'))
session = LocalProxy(partial(_lookup_req_object, 'session'))
g = LocalProxy(partial(_lookup_app_object, 'g'))
发现请求上下文(_request_ctx_stack) 和 应用上下文 (_app_ctx_stack) 都是LocalStack类的对象。LocalStack这个单词可以分为Local和Stack两个部分,顾名思义LocalStack:是封装Local实现线程隔离的栈结构。如下代码可以验证LocalStack是一个线程隔离的栈结构:
# LcoalStack线程隔离的栈
import time
import threading
from werkzeug.local import LocalStack
my_stack = LocalStack()
my_stack.push(1)
print("主线程中,栈顶元素为{}".format(str(my_stack.top)))
def worker():
print("在push之前,子线程中的栈顶元素为{}".format(str(my_stack.top)))
my_stack.push(2)
print("在push之后,子线程中的栈顶元素为{}".format(str(my_stack.top)))
t1 = threading.Thread(target=worker)
t1.start()
time.sleep(1)
print("最后,主线程中的栈顶元素为{}".format(str(my_stack.top)))
输出结果:
主线程中,栈顶元素为1
在push之前,子线程中的栈顶元素为None
在push之后,子线程中的栈顶元素为2
最后,主线程中的栈顶元素为1
5
flask线程隔离技术
由之前写过的文章得知current_app、request、session、g等对应都是指向相应栈的栈顶,当我们接受到一个请求里,flask才会把相应的app对象和新创建的Request对象压入相应的栈。由此,我们就知道了flask是怎么实现的多个请求,request的线程隔离。最后总结一下,字典、Local、LocalStack三者的关系,Local使用字典的方式实现的线程隔离,LocalStack封装了Local对象,并把它作为了自己的一个属性,从而实现了线程隔离的栈结构