在使用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() 运行结果: <Thread(Thread-2, started 123145372971008)> 100 <Thread(Thread-6, started 123145393991680)> 100 <Thread(Thread-1, started 123145367715840)> 100 <Thread(Thread-3, started 123145378226176)> 100 <Thread(Thread-5, started 123145388736512)> 100 <Thread(Thread-7, started 123145399246848)> 100 <Thread(Thread-4, started 123145383481344)> 100 <Thread(Thread-10, started 123145415012352)> 100 <Thread(Thread-8, started 123145404502016)> 100 <Thread(Thread-9, started 123145409757184)> 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() 运行结果: <Thread(Thread-2, started 123145483571200)> 888 <Thread(Thread-5, started 123145499336704)> 908 <Thread(Thread-3, started 123145488826368)> 930 <Thread(Thread-4, started 123145494081536)> 937 <Thread(Thread-1, started 123145478316032)> 941 <Thread(Thread-6, started 123145504591872)> 947 <Thread(Thread-7, started 123145509847040)> 949 <Thread(Thread-8, started 123145515102208)> 955 <Thread(Thread-9, started 123145520357376)> 962 <Thread(Thread-10, started 123145525612544)> 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() 运行结果: <Thread(Thread-4, started 123145570172928)> 100 <Thread(Thread-6, started 123145580683264)> 100 <Thread(Thread-1, started 123145554407424)> 100 <Thread(Thread-2, started 123145559662592)> 100 <Thread(Thread-8, started 123145591193600)> 100 <Thread(Thread-5, started 123145575428096)> 100 <Thread(Thread-3, started 123145564917760)> 100 <Thread(Thread-7, started 123145585938432)> 100 <Thread(Thread-10, started 123145601703936)> 100 <Thread(Thread-9, started 123145596448768)> 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 0x10407bd00> <class ‘_thread._local‘> 123 abc <_thread._local object at 0x10407bd00> 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‘。