在使用threading.local()之前,先了解一下局部变量和全局变量。
局部变量:
import threading
import time
def worker():
x = 0
for i in range(100):
time.sleep(0.0001)
x += 1
print(threading.current_thread(),x)
for i in range(10):
threading.Thread(target=worker).start()
运行结果:
100
100
100
100
100
100
100
100
100
100
上面例子使用多线程,每个子线程完成不同的计算任务,x是局部变量。
每个子线程都要压栈,每个栈是独立的空间。每次压栈,局部变量x的作用域地址是不同的(线程独享),计算结果互不干扰。
全局变量:
使用global:
import threading
import time
x = 0
def worker():
global x
x = 0
for i in range(100):
time.sleep(0.0001)
x += 1
print(threading.current_thread(),x)
for i in range(10):
threading.Thread(target=worker).start()
运行结果:
888
908
930
937
941
947
949
955
962
964
上面例子中当主线程中x是全局变量时,就变成了公共资源(也就是同一个对象),每个子线程互相干扰,最终导致错误的计算结果。
Python提供了 threading.local 类,将这个类实例化得到一个全局对象,但是不同的线程使用这个对象存储的数据其它线程不可见(本质上就是不同的线程使用这个对象时为其创建一个独立的字典)。
使用threading.local() :
import threading
import time
# class A:
# def __init__(self,x):
# self.x = x
# a = A(0)
a = threading.local()#全局对象
def worker():
a.x = 0
for i in range(100):
time.sleep(0.0001)
a.x += 1
print(threading.current_thread(),a.x)
for i in range(10):
threading.Thread(target=worker).start()
运行结果:
100
100
100
100
100
100
100
100
100
100
每个子线程使用全局对象a,但每个线程定义的属性a.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> 123
abc
Exception in thread Thread-1:
Traceback (most recent call last):
File "/Users/ihoney/Python/test_4.py", line 12, 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'。