Python中的ThreadLocal变量
我們知道多線程環境下,每一個線程均可以使用所屬進程的全局變量。如果一個線程對全局變量進行了修改,將會影響到其他所有的線程。為了避免多個線程同時對變量進行修改,引入了線程同步機制,通過互斥鎖,條件變量或者讀寫鎖來控制對全局變量的訪問。
在多線程環境下,每個線程都有自己的數據。一個線程使用自己的局部變量比使用全局變量好,因為局部變量只有線程自己能看見,不會影響其他線程,而全局變量的修改必須加鎖。
有時候使用局部變量不太方便,比如函數之間互相調用的時候,參數的傳遞,這時候如果使用全局變量也不行,因為每個線程處理的對象不同,因此 python 還提供了 ThreadLocal 變量,它本身是一個全局變量,但是每個線程卻可以利用它來保存屬于自己的私有數據,這些私有數據對其他線程也是不可見的。這真是一舉兩得的事。
下圖形象的給出了這幾個變量之間的關系:
從圖中可以看出進程包含線程,不同的線程可以使用同一個全局變量,但全局變量的改動也會影響到其他的線程,此時有了threadlocal變量的出現,即解決了全局變量的問題,又解決了不同線程間的局部變量改變的問題,互不影響。
全局 VS 局部變量
1、關于多線程下的全局變量的同步問題及線程鎖的作用見:
多線程和Lock
2、在線程中使用局部變量則不存在這個問題,因為每個線程的局部變量不能被其他線程訪問。
# coding=utf-8 import threading,time def show(num):print ('i am %s num=%s \n'% (threading.current_thread().getName(), num))def thread_cal():local_num = 0 # 局部變量for _ in range(5):#time.sleep(2)local_num += 1show(local_num)threads = [] for i in range(5):threads.append(threading.Thread(target=thread_cal))threads[i].start() for i in range(5):threads[i].join() # 阻礙主進程print('main process end!')運行結果:
i am Thread-1 num=1 i am Thread-2 num=1 i am Thread-5 num=1 i am Thread-3 num=1 i am Thread-4 num=1 i am Thread-1 num=2 i am Thread-2 num=2 i am Thread-5 num=2 i am Thread-3 num=2 i am Thread-4 num=2 ... ...i am Thread-1 num=5 i am Thread-2 num=5 i am Thread-5 num=5 i am Thread-3 num=5 i am Thread-4 num=5 main process end!可以看出這里每個線程都有自己的 local_num,各個線程之間互不干涉。
上面程序中我們需要給 show 函數傳遞 local_num 局部變量,并沒有什么不妥。不過考慮在實際生產環境中,我們可能會調用很多函數,每個函數都需要很多局部變量,這時候用傳遞參數的方法會很不友好。
為了解決這個問題,一個直觀的的方法就是建立一個全局字典,保存進程 ID 到該進程局部變量的映射關系,運行中的線程可以根據自己的 ID 來獲取本身擁有的數據。這樣,就可以避免在函數調用中傳遞參數,如下示例:
# coding=utf-8 import threading,timeglobal_data = {} def show():cur_thread = threading.current_thread()print ('i am %s num=%s \n'% (cur_thread.getName(), global_data[cur_thread]))def thread_cal():global global_datacur_thread = threading.current_thread()global_data[cur_thread] = 0for i in range(5):#time.sleep(2)global_data[cur_thread] += 1show()threads = [] for i in range(5):threads.append(threading.Thread(target=thread_cal))threads[i].start() for i in range(5):threads[i].join() # 阻礙主進程print('main process end!')實驗結果同上個實驗結果
保存一個全局字典,然后將線程標識符作為key,相應線程的局部數據作為 value,這種做法略顯繁瑣。而且這里并沒有真正做到線程之間數據的隔離,因為每個線程都可以讀取到全局的字典,每個線程都可以對字典內容進行更改。
為了更好解決這個問題,python 線程庫實現了 ThreadLocal 變量(很多語言都有類似的實現,比如Java)。ThreadLocal 真正做到了線程之間的數據隔離,并且使用時不需要手動獲取自己的線程 ID,如下示例:
global_data=threading.local()def show():cur_thread=threading.current_thread() print ('i am %s num=%s \n' % (cur_thread.getName() ,global_data.num))def thread_cal():global_data.num=0#cur_thread=threading.current_thread()#global_data[cur_thread]=0for i in range(5):global_data.num+=1 time.sleep(1)show()threads = [] for i in range(5):threads.append(threading.Thread(target=thread_cal))threads[i].start() for i in range(5):threads[i].join() # 阻礙主進程print('main thread:',global_data.__dict__) # {}實驗結果:
i am Thread-1 num=1 i am Thread-2 num=1 i am Thread-3 num=1 i am Thread-4 num=1 i am Thread-5 num=1 i am Thread-1 num=2 i am Thread-2 num=2 i am Thread-3 num=2 i am Thread-4 num=2 i am Thread-5 num=2 i am Thread-1 num=3 i am Thread-2 num=3 i am Thread-3 num=3 i am Thread-4 num=3 i am Thread-5 num=3 i am Thread-1 num=4 i am Thread-2 num=4 i am Thread-3 num=4 i am Thread-4 num=4 i am Thread-5 num=4 i am Thread-1 num=5 i am Thread-2 num=5 i am Thread-3 num=5 i am Thread-4 num=5 i am Thread-5 num=5 main thread: {}上面示例中每個線程都可以通過 global_data.num 獲得自己獨有的數據,并且每個線程讀取到的 global_data 都不同,真正做到線程之間的隔離。
參考:
http://python.jobbole.com/86150/
總結
以上是生活随笔為你收集整理的Python中的ThreadLocal变量的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: zulutrade
- 下一篇: python分布式进程(windows下