threading.local的使用
from threading import local
多个线程操作同一个变量,如果不加锁,会出现数据错乱问题,但是 读个线程同时操作 threading.local 对象 就不会出现数据错乱
作用: 线程变量,意思是threading.local中填充的变量属于当前线程,该变量对其他线程而言是隔离的,也就是说该变量是当前线程独有的变量,threading.local为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变懒
不使用threading.local之前
由于多线程共享全局变量,所以一个线程拿到的全局变量的是未必是当时自己修改后的值,有可能在拿到全局变量之前其他线程也对该全局变量进行了修改,原因是线程的调度室友操作系统决定的
from threading import Thread, get_ident
import time
# 全局变量
num = 0
def task(arg):
global num
num += 1
time.sleep(1)
# get_ident()返回的是每一个线程的唯一标识
print('第%s条线程的值为:' % (get_ident()), num)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
'''
由于每个线程只对全局变量做了一次修改
我们中间设置了1秒的延时
所以10个线程打印的全局变量的值都是最后修改的值,而并不是当时线程自己修改后的值。
'''
threading.local的基本使用
from threading import Thread, get_ident, local
import time
local_obj = local()
def task(arg):
# get_ident()返回的是每一个线程的唯一标识
print(get_ident())
# 在线程执行到此处时,为每一个线程开辟空间用来存储对象的值
local_obj.value = arg
time.sleep(1)
print('第%s条线程的值为:' % (get_ident()), local_obj.value)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
'''
由于创建了threading.local对象
当为对象赋值属性的时候,会为每个线程开辟一块空间来存储对象的属性值,空间与空间之间数据是隔离的
这样最终打印的时候就是线程自己空间中保存的数据的值
'''
自定义local对象,基于函数
flask 的request,和session 都是全局的,但是我们在不同的视图函数中使用的 是针对于当前这次请求的对象,它的底层就是基于local写的
flask部署支持多进程线程架构,也支持协程架构,flask内部重写了local,让它支持线程和协程
import time
from threading import get_ident, Thread
# 构造一个全局字典
storage = {}
def set(k, v):
# 获取当前字典
ident = get_ident()
if ident in storage:
# 如果当前线程id存在就修改id对应的值
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)
time.sleep(1)
print(get('val'))
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
自定义local对象,基于面向对象(支持线程与协程)
import time
# 支持线程与协程
try:
# 如果用的是协程的,获取的就是协程的id号
from greenlet import getcurrent as get_ident
except Exception as e:
# 如果用的是线程的,那么获取到的就是线程的id号
from threading import get_ident
from threading import Thread
class CustomLocal(object):
"""
自定义local对象,基于面向对象
为每个线程(协程)开辟一块空间进行数据的存储
空间与空间之间是隔离的
"""
def __init__(self):
# self.storage = {} # 执行此句代码的时候会先触发__setattr__方法
# 为了避免报错:RecursionError: maximum recursion depth exceeded while calling a Python object
# 需要先把storage创建出来,所以调用父类的__setattr__方法
# super(CustomLocal, self).__setattr__("storage", {})
object.__setattr__(self, 'storage', {}) # 父类调用类方法,类来调用变成普通函数,有几个值传入几个值,放入self里面去,后面用self.storage
def __setattr__(self, key, value):
ident = get_ident()
if ident in self.storage:
self.storage[ident][key] = value
else:
self.storage[ident] = {key: value} # 执行此句的时候又会触发__setattr__方法,所有就进入了死循环
def __getattr__(self, item):
ident = get_ident() # 看是获取到的是线程的还是携程的id号
return self.storage[ident][item]
local = CustomLocal()
def task(arg):
local.var = arg
time.sleep(1)
print(local.var)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()