先说一下和flask没有关系的:

我们都知道线程是由进程创建出来的,CPU实际执行的也是线程,那么线程其实是没有自己独有的内存空间的,所有的线程共享进程的资源和空间,共享就会有冲突,对于多线程对同一块数据处理的冲突问题,一个办法就是加互斥锁,另一个办法就是利用threadlocal

ThreadLocal   实现的思路就是给一个进程中的多个线程开辟空间来保存线程中特有的值

代码实现:

1、简单示例:

  1. import threading
  2. # 实例化对象
  3. local_values = threading.local()
  4. def func(num):
  5. # 给对象加属性,这个属性就会保存在当前线程开辟的空间中
  6. local_values.name = num
  7. import time
  8. time.sleep(1)
  9. # 取值的时候也从当前线程开辟的空间中取值
  10. print(local_values.name, threading.current_thread().name)
  11. for i in range(20):
  12. th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
  13. th.start()

打印结果:

  1. 0 线程0
  2. 1 线程1
  3. 2 线程2
  4. 3 线程3
  5. 10 线程10
  6. 9 线程9
  7. 4 线程4
  8. 8 线程8
  9. 5 线程5
  10. 7 线程7
  11. 6 线程6
  12. 13 线程13
  13. 11 线程11
  14. 17 线程17
  15. 15 线程15
  16. 14 线程14
  17. 16 线程16
  18. 12 线程12
  19. 18 线程18
  20. 19 线程19

如果把对象换成一个类对象:

  1. import threading
  2. # 如果是一个类对象,结果就完全不一样
  3. class Foo(object):
  4. def __init__(self):
  5. self.name = 0
  6. local_values = Foo()
  7. def func(num):
  8. local_values.name = num
  9. import time
  10. time.sleep(1)
  11. print(local_values.name, threading.current_thread().name)
  12. for i in range(20):
  13. th = threading.Thread(target=func, args=(i,), name='线程%s' % i)
  14. th.start()

打印结果:

  1. 19 线程1
  2. 19 线程3
  3. 19 线程4
  4. 19 线程5
  5. 19 线程2
  6. 19 线程0
  7. 19 线程9
  8. 19 线程6
  9. 19 线程8
  10. 19 线程11
  11. 19 线程7
  12. 19 线程10
  13. 19 线程15
  14. 19 线程16
  15. 19 线程13
  16. 19 线程14
  17. 19 线程18
  18. 19 线程19
  19. 19 线程12
  20. 19 线程17

2、依据这个思路,我们自己实现给线程开辟独有的空间保存特有的值

协程和线程都有自己的唯一标识get_ident,利用这个唯一标识作为字典的key,key对应的value就是当前线程或协程特有的值,取值的时候也拿这个key来取

  1. import threading
  2. # get_ident就是获取线程或协程唯一标识的
  3. try:
  4. from greenlet import getcurrent as get_ident # 协程
  5. # 当没有协程的模块时就用线程
  6. except ImportError:
  7. try:
  8. from thread import get_ident
  9. except ImportError:
  10. from _thread import get_ident # 线程
  11. class Local(object):
  12. def __init__(self):
  13. self.storage = {}
  14. self.get_ident = get_ident
  15. def set(self,k,v):
  16. ident = self.get_ident()
  17. origin = self.storage.get(ident)
  18. if not origin:
  19. origin = {k:v}
  20. else:
  21. origin[k] = v
  22. self.storage[ident] = origin
  23. def get(self,k):
  24. ident = self.get_ident()
  25. origin = self.storage.get(ident)
  26. if not origin:
  27. return None
  28. return origin.get(k,None)
  29. # 实例化自定义local对象对象
  30. local_values = Local()
  31. def task(num):
  32. local_values.set('name',num)
  33. import time
  34. time.sleep(1)
  35. print(local_values.get('name'), threading.current_thread().name)
  36. for i in range(20):
  37. th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
  38. th.start()

3、因为要不停的赋值取值,就想到了__setattr__和__getattr__方法,但是要注意初始化时赋值和__setattr__方法中赋值又可能产生递归的问题

  1. import threading
  2. try:
  3. from greenlet import getcurrent as get_ident # 协程
  4. except ImportError:
  5. try:
  6. from thread import get_ident
  7. except ImportError:
  8. from _thread import get_ident # 线程
  9. class Local(object):
  10. def __init__(self):
  11. # 这里一定要用object来调用,因为用self调用的就会触发__setattr__方法,__setattr__方法里
  12. # 又会用self去赋值就又会调用__setattr__方法,就变成递归了
  13. object.__setattr__(self, '__storage__', {})
  14. object.__setattr__(self, '__ident_func__', get_ident)
  15. def __getattr__(self, name):
  16. try:
  17. return self.__storage__[self.__ident_func__()][name]
  18. except KeyError:
  19. raise AttributeError(name)
  20. def __setattr__(self, name, value):
  21. ident = self.__ident_func__()
  22. storage = self.__storage__
  23. try:
  24. storage[ident][name] = value
  25. except KeyError:
  26. storage[ident] = {name: value}
  27. def __delattr__(self, name):
  28. try:
  29. del self.__storage__[self.__ident_func__()][name]
  30. except KeyError:
  31. raise AttributeError(name)
  32. local_values = Local()
  33. def task(num):
  34. local_values.name = num
  35. import time
  36. time.sleep(1)
  37. print(local_values.name, threading.current_thread().name)
  38. for i in range(20):
  39. th = threading.Thread(target=task, args=(i,),name='线程%s' % i)
  40. th.start()

我们再说回Flask,我们知道django中的request是直接当参数传给视图的,这就不存在几个视图修改同一个request的问题,但是flask不一样,flask中的request是导入进去的,就相当于全局变量,每一个视图都可以对它进行访问修改取值操作,这就带来了共享数据的冲突问题,解决的思路就是我们上边的第三种代码,利用协程和线程的唯一标识作为key,也是存到一个字典里,类中也是采用__setattr__和__getattr__方法来赋值和取值

分情况:

对于单进程单线程:没有影响,基于全局变量

对于单进程多线程:利用threading.local()  对象

对于单进程单线程的多协程:本地线程对象就做不到了,要用自定义,就是上边的第三个代码

一共涉及到四个知识点:

1、唯一标识

2、本地线程对象

3、setattr和getattr

4、怎么实现

Flask中的ThreadLocal本地线程,上下文管理的更多相关文章

  1. flask你一定要知道的上下文管理机制

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

  2. ThreadLocal本地线程变量的理解

     一般的Web应用划分为展现层.服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用.在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程.       ...

  3. Hibernate中Session与本地线程绑定

    ------------------siwuxie095 Hibernate 中 Session 与本地线程绑定 1.Session 类似于 JDBC 的连接 Connection 2.Session ...

  4. Flask(3)- flask中的CBV、werkzeug+上下文初步解读、偏函数和线程安全

    一.flask中的CBV 对比django中的CBV,我们来看一下flask中的CBV怎么实现? from flask import Flask, render_template, url_for, ...

  5. ThreadLocal = 本地线程?

    一.定义 ThreadLocal是JDK包提供的,从名字来看,ThreadLocal意思就是本地线程的意思. 1.1 是什么? 要想知道他是个啥,我们看看ThreadLocal的源码(基于JDK 1. ...

  6. python2.7高级编程 笔记一(Python中的with语句与上下文管理器学习总结)

    0.关于上下文管理器上下文管理器是可以在with语句中使用,拥有__enter__和__exit__方法的对象. with manager as var: do_something(var) 相当于以 ...

  7. Java 类 ThreadLocal 本地线程变量

    前言:工作中将要使用ThreadLocal,先学习总结一波.有不对的地方欢迎评论指出. 定义 ThreadLocal并不是一个Thread,而是Thread的局部变量.这些变量不同于它们的普通对应物, ...

  8. Python中的with语句(上下文管理协议)

    在平时工作中总会有这样的任务,它们需要开始前做准备,然后做任务,然后收尾清理....比如读取文件,需要先打开,读取,关闭 这个时候就可以使用with简化代码,很方便 1.没有用with语句 f = o ...

  9. Java Concurrency - ThreadLocal, 本地线程变量

    共享数据是多线程应用最常见的问题之一,但有时我们需要为每个线程保存一份独立的变量.Java API 提供了 ThreadLocal 来解决这个问题. 一个 ThreadLocal 作用的例子: imp ...

随机推荐

  1. js 技巧 (二)

    //最小化,最大化,关闭 <object id=min classid="clsid:ADB880A6-D8FF-11CF-9377-00AA003B7A11">  & ...

  2. Win2008 Server搭建FTP服务器

    首先创建一个专门的FTP用户,当然也可以不创建. 用系统自带的超管用户. 设置用户名和密码.用户下次登陆必须修改密码记得去掉勾选. 在角色里面的WEB服务器找到添加角色服务.我之前有安装IIS. 没有 ...

  3. MyBatis 多参问题

    当传入的参数为多个参数时 1 可以不封装为Javabean直接传入,写法如下 public List<XXXBean> getXXXBeanList(String xxId, String ...

  4. NioEventLoopGroup中的nThreads和executor

    NioEventLoopGroup只传入nThreads即可,创建nThreads个NioEventLoop,boss为NioEventLoop注册建立的channel时,使用默认的ThreadPer ...

  5. Linux虚拟机安装学习笔记

    一.Linux系统的安装1.VMwaer虚拟机的安装使用 官方下载软件地址:www.vmwaer.com 安装的虚拟机可以与现实的计算机进行通信 安装虚拟主机可以随意定制硬件安装配置建议: CPU:1 ...

  6. 解决webview.getFavicon()返回值总是为空的问题

    在webview中,我们需要获取网站的favicon.ico图标,但是默认状态下,WebChromeClient中的onReceivedIcon方法获取到的icon总是为null; webview.g ...

  7. hdu - 2066 一个人的旅行(基础最短路)

    http://acm.hdu.edu.cn/showproblem.php?pid=2066 把与草儿相连的城市最短距离置为0,然后进行dijkstra,在t个城市里找出距离最近的一个即可. #inc ...

  8. HDU 5644 King's Pliot【费用流】

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5644 题意: 每天都有p[i]个飞行员进行阅兵,飞行员只工作一天. m个休假公式,花费tt[i]元让 ...

  9. [bzoj3238][Ahoi2013]差异_后缀数组_单调栈

    差异 bzoj-3238 Ahoi-2013 题目大意:求任意两个后缀之间的$LCP$的和. 注释:$1\le length \le 5\cdot 10^5$. 想法: 两个后缀之间的$LCP$和显然 ...

  10. excel 合并 单元格内容

    刚刚有人问怎么合并单元格内容,正好excel 我也不会,顺便查查记录一下 1.假设有两个单元格如下:           单元格1 单元格2           2. 在一个空白单元格输入 =( 这代 ...