Flask之上下文管理

知识储备之问题情境:

request中的参数:

  • 单进程单线程
  • 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.local对象
  • 单进程单线程(多协程)threading.local对象做不到(因为一个线程下多个协程同享一个线程的资源)

解决办法:

​ 自定义类似threading.local对象(支持协程)---保证多协程下数据的安全

先来看一下下面这段代码(支持多线程):

# -*- coding: utf-8 -*-

"""
1288::{} """ from _thread import get_ident
import threading 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
# 将k,v 保存到 storage中 形式如下
# {
# 1023:{k,v}, # self.storage[ident] = origin 所添加的值
# 1045:{k1,v1} # 原先storage中有的值 # }
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_obj = Local() # 获取每一个线程的唯一标识
def task(num):
local_obj.set('name',num)
import time
time.sleep(1)
print(local_obj.get('name'),threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线程%s' % i)
th.start()
"""
0 线程0
1 线程1
2 线程2
5 线程5
6 线程6
3 线程3
4 线程4
10 线程10
9 线程9
11 线程11
7 线程7
13 线程13
14 线程14
17 线程17
18 线程18
15 线程15
19 线程19
8 线程8
12 线程12
16 线程16
"""

再进一步,支持协程

# 首先需要安装依赖
pip3 intall gevent # gevent 依赖安装 greenlet 可以获取协程的唯一标识 # -*- coding: utf-8 -*- """
1288::{ } """ 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
# 将k,v 保存到 storage中 形式如下
# {
# 1023:{k,v}, # self.storage[ident] = origin 所添加的值
# 1045:{k1,v1} # 原先storage中有的值 # }
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_obj = Local() # 获取每一个线程的唯一标识
def task(num):
local_obj.set('name', num)
import time
time.sleep(1)
print(local_obj.get('name'), threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线/协程%s' % i)
th.start()

flask中实现的方式

flask中运用了面向对象的一些方法重试简化了实现方式

先补充了解面向对象的姿势:

class Foo():

    # 在执行 对象.属性 = 值的时候执行,这里可以写赋值操作
def __setattr__(self,key,value):
print(key,value)
# 在执行 对象.属性的时候,执行, 这里可以写获取对象的属性
def __getattr__(self, item):
print(item)
foo = Foo()
foo.x = 123
foo.x

但是还是有点问题 上面写法: 如果在 初始化操作的时候,会出现递归问题

class Foo():
def __init__(self):
self.storage ={} def __setattr__(self,key,value):
self.storage = {'k':'v'}
print(key,value) def __getattr__(self, item):
print(item)
foo = Foo()
foo.x = 123
foo.x """
上述办法 会在 __setattr__ 这里产生递归
self.storage = {'k':'v'}
[Previous line repeated 327 more times]
RecursionError: maximum recursion depth exceeded
"""

解决办法

class Foo(object):
def __init__(self):
object.__setattr__(self, "storage", {})
# self.storage = {} def __setattr__(self, key, value):
storage = self.storage
storage['1024'] = {key: value} print(storage) def __getattr__(self, item):
print(item)
""" {'1024': {'x': 123}}
x
"""

上述问题 接近源码的做法实现一个支持协程线程的自定义类似threading.local 对象

# -*- coding: utf-8 -*-

"""
模仿
flask中运用了一些面向对象的方法: __getattr__,__setattr__ """ 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} # 获取一个线程对象
local_obj = Local() # 获取每一个线程的唯一标识
def task(num):
local_obj.name = num
import time
time.sleep(1)
print(local_obj.name, threading.current_thread().name) for i in range(20):
th = threading.Thread(target=task, args=(i,), name='线程%s' % i)
th.start()
"""
0 线程0
3 线程3
4 线程4
1 线程1
2 线程2
8 线程8
7 线程7
5 线程5
6 线程6
10 线程10
9 线程9
11 线程11
12 线程12
15 线程15
14 线程14
13 线程13
19 线程19
16 线程16
18 线程18
17 线程17
"""

flask 源码实现方式

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 __call__(self, proxy):
"""
当类实例化的对象 被 调用的时候执行该函数
"""
"""Create a proxy for a name."""
return LocalProxy(self, proxy) def __release_local__(self):
self.__storage__.pop(self.__ident_func__(), None) 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)

PS: flask 中保存请求相关 session相关的对象的在并发的时候的不同(保证数据的安全),都是基于这个 threading.local 实现的

flask上下文管理之threading.local的更多相关文章

  1. flask上下文管理相关 - threading.local 以及原理剖析

    threading.local 面向对象相关: setattr/getattr class Foo(object): pass obj = Foo() obj.x1 = 123 # object.__ ...

  2. Flask 上下文管理

    为什么用threading.local? 我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程 ...

  3. Flask上下文管理机制

    前引 在了解flask上下文管理机制之前,先来一波必知必会的知识点. 面向对象双下方法 首先,先来聊一聊面向对象中的一些特殊的双下划线方法,比如__call__.__getattr__系列.__get ...

  4. Flask上下文管理、session原理和全局g对象

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  5. Flask上下文管理

    一.一些python的知识 1.偏函数 def add(x, y, z): print(x + y + z) # 原本的写法:x,y,z可以传任意数字 add(1,2,3) # 如果我要实现一个功能, ...

  6. Flask上下文管理及源码刨析

    基本流程概述 - 与django相比是两种不同的实现方式. - django/tornado是通过传参数形式实现 - 而flask是通过上下文管理, 两种都可以实现,只不实现的方式不一样罢了. - 上 ...

  7. flask 上下文管理 &源码剖析

    基本流程概述 - 与django相比是两种不同的实现方式. - django/tornado是通过传参数形式实现 - 而flask是通过上下文管理, 两种都可以实现,只不实现的方式不一样罢了. - 上 ...

  8. Flask 上下文管理-- (session,request,current_app的传递)--类似本地线程实现,以及多app应用

    Flask session,request,current_app的传递 请求上下文的作用 -- 封装请求相关得数据(request,session) 请求上下文 request session re ...

  9. Flask上下文管理机制流程(源码剖析)

    Flask请求上下文管理 1 偏函数 partial 使用该方式可以生成一个新函数 from functools import partial def mod( n, m ): return n % ...

随机推荐

  1. Python散列类型和运算符

    集合定义 集合的交 并 差 常见的运算符的用法 字典的定义 字典的 get  items  keys  pop  popitem  update  方法 三种逻辑运算 集合 集合特性 唯一性:不存在两 ...

  2. pycharm添加断点,分段运行,以及继续运行;

    1.打断点,只需在代码的前面单机左键即可:2.运行,点击Debug...(如图,像贝壳一样的图标),只会运行第一个红点前的代码: 3.此时若想将第2个红点前的代码运行完毕:点击Resume Progr ...

  3. input 控件常用属性

  4. 【Spring AOP】AOP实现原理(六)

    原文链接:https://my.oschina.net/guangshan/blog/1797461

  5. LG2495 「SDOI2011」消耗战 虚树

    问题描述 LG2495 题解 虚树 \(\mathrm{Code}\) #include<bits/stdc++.h> using namespace std; #define int l ...

  6. Excel日历控件实现下拉选取日期含VB代码实现

    以下是Excel2016通过安装控件,实现表格下拉选择日期的一些步骤: 知识准备工作:先了解下如何安装控件,这一部分很重要,excel选择可用宏https://jingyan.baidu.com/ar ...

  7. vue 路由跳转到本页面,ts 监听路由变化

    @Watch('$route') routechange(to: any, from: any) { //参数不相等 if (to.query.name!=from.query.name) { //t ...

  8. CDN的智能调度,链路优化的详细解答

    您的用户在请求资源的过程中,可能受到网络.地域.带宽等影响,无法保证请求一定是按照最优访问路径进行传递,猫云 CDN 通过对全网链路进行实时监控,结合自研的 GSLB 调度体系和智能路由技术,从以下几 ...

  9. Anaconda--在虚拟环境中安装CUDA and cudnn

    在conda虚拟环境中安装CUDAconda install cudatoolkit=8.0 -c https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs ...

  10. laravel报错:SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '0' for key 'PRIMARY' (SQL: insert into `cart` (`uid`, `gid`, `gname`, `price`) values (3, 21, 夏季日系复古工装短袖衬衫男士印花潮流宽松五分

    原因:要操作的数据表id没有设置自增,导致出现id为0的情况 解决方法:给该数据表的id字段设置自增