在Flask请求上下文中,我们发现Flask中current_app, g这两个对象以及request,session这两个对象,在整个Flask生命周期中,都只是一个对象,那当请求过来的时候,是怎么区分是哪个用户的呢?

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'))

这里面主要用到了一个线程里面的Local对象以及偏函数partial

Local

在使用threading.local()之前,先了解一下局部变量和全局变量。

局部变量

import threading
import time def foo():
x = 0
for i in range(100):
time.sleep(0.0001)
x += 1 print(threading.current_thread(), x) for i in range(5):
threading.Thread(target=foo).start() """
运行结果:
<Thread(Thread-5, started 21732)> 100
<Thread(Thread-1, started 13300)> 100
<Thread(Thread-4, started 1568)> 100
<Thread(Thread-2, started 19864)> 100
<Thread(Thread-3, started 23984)> 100
"""

上面例子使用多线程,每个子线程完成不同的计算任务,x是局部变量。

每个子线程都要压栈,每个栈是独立的空间。每次压栈,局部变量x的作用域地址是不同的(线程独享),计算结果互不干扰。

全局变量

import threading
import time x = 0 def foo():
global x
x = 0
for i in range(100):
time.sleep(0.0001)
x += 1 print(threading.current_thread(), x) for i in range(5):
threading.Thread(target=foo).start() """
运行结果:
<Thread(Thread-1, started 19492)> 491
<Thread(Thread-3, started 22692)> 497
<Thread(Thread-5, started 24344)> 498
<Thread(Thread-2, started 24428)> 499
<Thread(Thread-4, started 19000)> 500
"""

上面例子中当主线程中x是全局变量时,就变成了公共资源(也就是同一个对象),每个子线程互相干扰,最终导致错误的计算结果。

Python提供了 threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其它线程不可见(本质上就是不同的线程使用这个对象时为其创建一个独立的字典)。

使用threading.local()

import threading
import time loc = threading.local() def foo():
loc.x = 0
for i in range(100):
time.sleep(0.0001)
loc.x += 1 print(threading.current_thread(), loc.x) for i in range(5):
threading.Thread(target=foo).start() """
运行结果:
<Thread(Thread-1, started 20008)> 100
<Thread(Thread-2, started 23644)> 100
<Thread(Thread-5, started 10396)> 100
<Thread(Thread-4, started 22280)> 100
<Thread(Thread-3, started 19980)> 100
"""

每个子线程使用全局对象loc,但每个线程定义的属性loc.x是该线程独有的。

举一个错误的例子:,主线程中使用threading.local定义本地变量x,x在主线程中是独有的,子线程中就访问不到主线程的x的属性。

import threading

X='abc'
ctx=threading.local()
ctx.x=123 #主线程中定义x本地属性
print(ctx,type(ctx),ctx.x) def work():
print(X)
print(ctx)
print(ctx.x) #子线程访问不到
print('Good job') threading.Thread(target=work).start() """
运行结果:
<_thread._local object at 0x000001B22BBAB780> <class '_thread._local'> 123
abc
<_thread._local object at 0x000001B22BBAB780>
Exception in thread Thread-1:
Traceback (most recent call last):
File "E:/Python学习笔记/flask/123.py", line 13, in work
print(ctx.x) # 子线程访问不到
AttributeError: '_thread._local' object has no attribute 'x'
"""

ctx全局对象对主线程和子线程都是可以使用的,主线程定义了属性x,但子线程在尝试访问属性x时,就相当于访问自己线程内的属性x,而自己线程并没有定义,就会抛出AttributeError异常:'_thread._local' object has no attribute 'x'

自定义threading.local

函数版

from threading import get_ident, Thread
import time # 定义一个全局字典
storage = {} def set(k, v):
ident = get_ident()
# print(ident)
if ident in storage:
storage[ident][k] = v
else:
storage[ident] = {k: v} def get(k):
ident = get_ident()
# print(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() print(storage)
"""
{
20552: {'val': 0},
18496: {'val': 1},
24476: {'val': 2},
18700: {'val': 3},
23740: {'val': 4},
22160: {'val': 5},
23896: {'val': 6},
19204: {'val': 7},
19028: {'val': 8},
17972: {'val': 9}
}
"""

面向对象版

from threading import get_ident,Thread
import time 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')
print(v) for i in range(10):
t = Thread(target=task,args=(i,))
t.start() print(Local.storage)
"""
{
19296: {'val': 0},
20436: {'val': 1},
8240: {'val': 2},
19668: {'val': 3},
16932: {'val': 4}
}
"""

通过setattr和getattr实现

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()
def task(arg):
obj.val = arg
print(obj.val) for i in range(10):
t = Thread(target=task,args=(i,))
t.start()

每个对象有自己的存储空间(字典)

from threading import get_ident, Thread
import time class Local(object): def __init__(self):
object.__setattr__(self, 'storage', {})
# print(1, self.__dict__)
# self.aaa = {}
# print(self.__dict__) def __setattr__(self, k, v):
# print(k, v, '>>>')
ident = get_ident()
# print(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()
# print(2, obj.__dict__) def task(arg):
obj.val = arg
obj.xxx = arg
print(obj.val)
# print(obj.__dict__) for i in range(10):
t = Thread(target=task, args=(i,))
t.start()

Flask补充--threading.local对象的更多相关文章

  1. flask上下文管理之threading.local

    Flask之上下文管理 知识储备之问题情境: request中的参数: 单进程单线程 单进程多线程-->reqeust 会因为多个请求,数据发生错乱.--->可以基于threading.l ...

  2. threading.local学习

    多线程抢占问题 import time import threading obj = 5 def task(arg): global obj obj = arg time.sleep(1) print ...

  3. 线程锁、threading.local(flask源码中用的到)、线程池、生产者消费者模型

    一.线程锁 线程安全,多线程操作时,内部会让所有线程排队处理.如:list/dict/Queue 线程不安全 + 人(锁) => 排队处理 1.RLock/Lock:一次放一个 a.创建10个线 ...

  4. local 对象补充

    昨日回顾 1 @app.before_first_request,再项目启动后接收到的第一个请求,会执行before_first_request,他再@app.before_request之前执行.他 ...

  5. flask之请求与响应、闪现(阅后即焚)、请求扩展(before,after)、中间件、LOCAL对象、偏函数、

    目录 1.flask请求与响应 2.闪现 3.请求扩展 4.中间件 5.LOCAL对象 6.偏函数 templates 1.flask请求与响应 from flask import Flask,req ...

  6. flask 源码专题(十一):LocalStack和Local对象实现栈的管理

    目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于localstack的实现 3. 总结 04 LocalS ...

  7. 04 flask源码剖析之LocalStack和Local对象实现栈的管理

    04 LocalStack和Local对象实现栈的管理 目录 04 LocalStack和Local对象实现栈的管理 1.源码入口 1. flask源码关于local的实现 2. flask源码关于l ...

  8. 六十七:flask上下文之Local线程隔离对象

    Local对象在flask中,类似于request对象,其实是绑定到了werkzeug.local.Local对象上,这样即使是同一个对象,在多线程中都是隔离的,类似的对象还有session以及g对象 ...

  9. threading.local在flask中的用法

    一.介绍 threading.local的作用: 多个线程修改同一个数据,复制多份变量给每个线程用,为每个线程开辟一块空间进行数据的存储,而每块空间内的数据也不会错乱. 二.不使用threading. ...

随机推荐

  1. 细数C++中的for循环

    1.for(;;)这个是最基础最简单的for循环,从刚开始学习C语言的时候就知道的.for(int i = 0; i < 10; ++i){ }2.foreach完整的是for each(obj ...

  2. 配置flutter For IOS

    https://www.cnblogs.com/lovestarfish/p/10628205.html 第一步,下载flutter最新版,解压到自己的目录里: 提供网址:https://flutte ...

  3. iOS9新框架—Watch Connectivity(详情:http://ios.itcast.cn/subject/ios9/index.shtml )

    1背景. Watch Connectivity--专为用来实现Watch与配对iPhone上的文件和数据的来回传输而生. 2.iOS8与iOS9发送数据的对比. 我们知道数据交换在iOS8中就有,我们 ...

  4. python中字符串

    字符串:可用单引号 双引号 三引号 来表示 可用来定义国籍,姓名,家庭住址等选项:#.join 把可迭代的对象转化成字符串 (字符串,列表,元组,字典等),列表 元组 合并为字符串,字典合并的是key ...

  5. Service__cmd安装MySQL并连接SQLyog

    整理记录关于使用cmd安装mysql的过程   1.配置环境变量 1) 计算机->属性->高级系统设置->环境变量 2)先添加变量 变量名:MYSQL_HOME 变量值:D:\mys ...

  6. eclipse C++ 配置自动提示

    转:http://www.cnblogs.com/myitm/archive/2010/12/17/1909194.html 定位到:Windows→Preferences→Java→Editor→C ...

  7. 安装npm install app-inspector -g 提示错误

    问题1: npm WARN optional SKIPPING OPTIONAL DEPENDENCY: fsevents@^ (node_modules\app-inspector\node_mod ...

  8. golang中的struct标签tag

    这个以前用beego时涉及过,时间久了,就忘了. 现在k8s里的controller,一样用了这个语法, 再拾起来吧. http://www.01happy.com/golang-struct-tag ...

  9. public class和class的区别

    class A { } class B { } public class 和class的区别: * 一个java源文件当中看看定义多个class *一个java源文件当中public的class不是必 ...

  10. 【UOJ388】配对树(dsu on tree+线段树)

    传送门 题意: 给出一颗含有\(n\)个结点的无根树,之后给出一个长度为\(m\)的序列,每个元素在\([1,n]\)之间. 现在序列中每个长度为偶数的区间的完成时间定义为树上最小配对方法中每对匹配点 ...