通过local对象的使用,分析flask中全局request对象为神魔不同的视图函数中不会混乱--协助解读flask的源码
问题::fask的request是全局的,为神魔每个视图函数都是用request,却没有造成request中数据错乱的现象??
# 不用local, 开启10个线程同时对全局的wzg修改,这种情况下,我看一下执行的结果会是怎样?
from threading import Thread
import time
wzg = -1
def task(arg):
global wzg
wzg = arg
# 阻塞一会,线程该值完毕,哪一个线程执行最后的到执行权,结果就会修改全局wzg,其他线程对结果的修改就会被篡改,所以结果全都一样
time.sleep(2)
print(wzg)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
'''
#结果全部为9,所以其他线程对wzg的修改被篡改,
造成这个情况的原因是:
多个线程同时操作全局里的同一个变量wzg,没有任何标记可以区分那个线程修改了什么,最后执行的线程会将前面其他线程修改的值全都次修该的值,造成结果的错乱
所以打印的结果也是最后线程修改的值
flask中的request,session也是全局变量,为神魔每个视图用的request不会乱?
这里先看一下local对象的使用
'''
#使用local对象
from threading import Thread
from threading import local
'''
根据线程进行区分,相当于这种存储方式{'线程id1':{'args':1},'线程id2':{'args':2},'线程id3':{'args':3}}
这样打印出来的就是对应线程的值,这样就可以防止数据错乱
# # {arg:1}
'''
import time
lqz = local()
def task(arg):
lqz.arg = arg
time.sleep(2)
print(lqz.arg)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
#自定义local,函数版
from threading import get_ident,Thread #get_ident线程的ID号
import time
storage = {}
def set(k,v):
#获取线程的id号
# storage={'线程id1': {'args': 1}, '线程id2': {'args': 2}, '线程id3': {'args': 3}}
#这样子存起来的值就对应各自的线程
ident = get_ident()
print(ident)
if ident in storage:
storage[ident][k] = v
else:
storage[ident] = {k:v}
def get(k):
ident = get_ident()
return storage[ident][k]
def task(arg):
set('val',arg)
v = get('val')
print(v)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
#面向对象版的local
from threading import get_ident,Thread
import time
#{'线程id1': {'args': 1}, '线程id2': {'args': 2}, '线程id3': {'args': 3}}
class Local(object):
storage = {}
def set(self, k, v):
ident = get_ident()
if ident in Local.storage:
Local.storage[ident][k] = v
else:
Local.storage[ident] = {k: v}
def get(self, k):
ident = get_ident()
return Local.storage[ident][k]
obj = Local()
def task(arg):
obj.set('val',arg)
v = obj.get('val')
time.sleep(1)
print(v)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
#改进版本1
from threading import get_ident,Thread
import time
class Local(object):
storage = {}
def __setattr__(self, k, v):
ident = get_ident()
if ident in Local.storage:
Local.storage[ident][k] = v
else:
Local.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return Local.storage[ident][k]
obj = Local()
# obj2=Local() #谢谢对象用的都是同一个字典
# obj3=Local()
# obj4=Local()
# obj5=Local()
def task(arg):
obj.val = arg
time.sleep(1)
print(obj.val)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
上面那个用的全是类的字典,不管生成几个对象,用的都是同一个,也有可能造成数据错乱
所以我们希望每次生成local对象用的都是自己的字典,所以对上面的继续改进2
改进版2
from threading import get_ident,Thread
import time
class Local(object):
def __init__(self): #将字典放在init中,这样每个对象用的都是自己的字典,但是要注意黄色部分的写法会出现一直递归,报错,
object.__setattr__(self,'storage',{}) #这里直接调用父类的__setattr__方法
#这样写会出现递归,该出调用__setattr__,se
# self.storage={}
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
# obj.xxx = arg
time.sleep(1)
print(obj.val)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()
到此为止,自己写了一个local对象,只支持线程
#改进版3即支持协程又支持线程
try:
#getcurrent 获取协程id
from greenlet import getcurrent as get_ident
except Exception as e:
from threading import get_ident
from threading import Thread
import time
class Local(object):
def __init__(self):
object.__setattr__(self,'storage',{})
def __setattr__(self, k, v):
ident = get_ident()
if ident in self.storage:
self.storage[ident][k] = v
else:
self.storage[ident] = {k: v}
def __getattr__(self, k):
ident = get_ident()
return self.storage[ident][k]
obj = Local()
def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
for i in range(10):
t = Thread(target=task,args=(i,))
t.start()