1. 不使用threading.local之前
由于多线程共享全局变量,所以一个线程拿到的全局变量的值未必是当时自己修改后的值,有可能在拿到全局变量之前其它线程也对该全局变量进行了修改,原因是线程的调度是由操作系统决定的,如下:
from threading import Thread, get_ident
import time
num = 0
def task(arg):
# get_ident()返回的就是每一个线程的唯一标识
print(get_ident())
global num
num += 1
time.sleep(1)
print(num)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
由于每个线程只对全局变量做了一次修改,我们中间设置了1秒的延时,所以10个线程打印的全局变量的值都是最后修改的值,而并不是当时线程自己修改后的值。
2. threading.local的基本使用
from threading import Thread, local, get_ident
import time
local_obj = local()
def task(arg):
"""
threading.local的作用:为每个线程开辟一块空间进行数据的存储
空间与空间之间数据是隔离的
"""
# get_ident()返回的就是每一个线程的唯一标识
print(get_ident())
# 线程执行到此的时候,为每一个线程开辟一块空间用来存储对象的值
local_obj.value = arg
time.sleep(1)
print(local_obj.value)
for i in range(10):
t = Thread(target=task, args=(i,))
t.start()
由于创建了threading.local对象,当为对象赋值属性的时候,会为每个线程开辟一块空间来存储对象的属性值,空间与空间之间数据是隔离的,这样最终打印的时候就是线程自己空间中保存的数据的值。
3. 自定义local对象,基于函数
import time
from threading import get_ident, Thread
storage = {}
def set(k, v):
ident = get_ident()
if ident in storage:
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()
4. 自定义local对象,基于面向对象:
import time
from threading import Thread, get_ident
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", {})
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()
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()
5. 自定义local对象,基于面向对象-升级版支持协程:
import time
try:
from greenlet import getcurrent as get_ident
except Exception as e:
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", {})
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()
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()