在使用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'。
