Flask上下文
在flask里的request和session的设置方式还是比较独特的,导入的这个两个东西给人感觉是全局变量,不像django,request是以参数方式传递,当然flask这种方式在单线程下,肯定是不会存在问题的,但是多线程了?
试想这么一个场景:Alex先来设置了session,过一阵子再来取值,但是这段时间seven改掉了这个值,而Alex来取的值变成了seven设置的,这并不是我们想要的,那基于多线程下,你猜flask是怎么解决这个问题了?
- 加锁,设置完后我先锁上,等下次用完再解锁
- 给线程开辟单独的空间,用于保存这个
如果是第一种方式,在执行效率上会大大折扣,所以猜测是第二种方式
在深入研读flask源码前,我需要了解一下本地线程,它保证了即使是多线程的情况下,自己的值也是相互隔离的
import threading
local_values = threading.local()
def func(num):
local_values.name = num #这个就是隔离的变量
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name)
for i in range(20):
th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
th.start()
所以上面的threading.local对象,用于为每个线程开辟一块空间来保存它独有的值
所以就有了以下几种情况下,解决方案:
- 情况一:单进程单线程,基于全局变量来做
- 情况二:单进程多线程,threading.local对象
- 情况三:单进程单线程(多个协程),那这个threading.local对象也做不到
对于情况三,由于协程是在线程里并发的,而threading.local对象只是对线程起作用,似乎用它对多协程不起作用,那说这里,如果要支持,就需要自定义了
在存储上,可以用字典来存储,但我们需要获取线程或协程的唯一标识
- 线程 from _thread import get_ident
- 协程 from greenlet import getcurrent as get_ident
"""
{
1368:{}
}
"""
import threading
try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程
class Local(object):
def __init__(self):
self.storage = {}
self.get_ident = get_ident
def set(self,k,v):
ident = self.get_ident()
origin = self.storage.get(ident)
if not origin:
origin = {k:v}
else:
origin[k] = v
self.storage[ident] = origin
def get(self,k):
ident = self.get_ident()
origin = self.storage.get(ident)
if not origin:
return None
return origin.get(k,None)
local_values = Local()
def task(num):
local_values.set('name',num)
import time
time.sleep(1)
print(local_values.get('name'), threading.current_thread().name)
for i in range(20):
th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
th.start()
上面方式就能完美的解决协程了
不过在上面的设置和获取,我们可以优化一下,那就要用到面向对象的知识了
class Foo(object):
def __init__(self):
object.__setattr__(self, 'storage', {}) #这赋值方式就不会调用__setattr__
# self.storage = {}
def __setattr__(self, key, value):
# self.storage = {'k1':'v1'} #在这里不能用这种方式,因为这个赋值过程就是在调用__setattr__,会陷入死循环
print(key,value)
def __getattr__(self, item):
print(item)
return 'df'
obj = Foo()
obj.x = 123 #调用__setattr__方法
# 对象.xx #调用__getattr__方法
优化后的就和flask实现的源码一致了
import threading
try:
from greenlet import getcurrent as get_ident # 协程
except ImportError:
try:
from thread import get_ident
except ImportError:
from _thread import get_ident # 线程
class Local(object):
def __init__(self):
object.__setattr__(self, '__storage__', {})
object.__setattr__(self, '__ident_func__', get_ident)
def __getattr__(self, name):
try:
return self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
def __setattr__(self, name, value):
ident = self.__ident_func__()
storage = self.__storage__
try:
storage[ident][name] = value
except KeyError:
storage[ident] = {name: value}
def __delattr__(self, name):
try:
del self.__storage__[self.__ident_func__()][name]
except KeyError:
raise AttributeError(name)
local_values = Local()
def task(num):
local_values.name = num
import time
time.sleep(1)
print(local_values.name, threading.current_thread().name)
for i in range(20):
th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
th.start()

浙公网安备 33010602011771号