2008-09-19 73 views
73

在Python中,具體來說,變量如何在線程之間共享?什麼是Python中的「線程本地存儲」,爲什麼我需要它?

雖然我在使用threading.Thread之前從未真正理解或查看過變量如何共享的示例。他們是在主線和孩子之間共享還是僅在孩子之間共享?我什麼時候需要使用線程本地存儲來避免這種共享?

我已經看到很多關於通過使用鎖來同步對線程間共享數據的訪問的警告,但我還沒有看到這個問題的一個很好的例子。

在此先感謝!

+1

標題與該問題不符。問題是在線程之間共享變量,標題暗示它特別針對線程本地存儲 – Casebash 2009-09-10 22:57:55

回答

57

在Python中,所有東西都是共享的,除了函數局部變量(因爲每個函數調用都有自己的一組本地變量,並且線程始終是獨立的函數調用。)即使如此,只有變量本身參考對象)是本地的功能;對象本身總是全局的,任何東西都可以引用它們。 特定線程的Thread對象在這方面不是特殊對象。如果將Thread對象存儲在所有線程都可以訪問的地方(如全局變量),則所有線程都可以訪問該對象。如果你想自動修改什麼,你不只是在這個相同的線程中創建,並沒有存儲任何地方的另一個線程可以得到它,你必須通過鎖來保護它。所有線程當然必須共享這個相同的鎖,否則它不會非常有效。

如果你想要真正的線程本地存儲,那就是threading.local進來。threading.local的屬性不在線程之間共享;每個線程只能看到它自己放在那裏的屬性。如果您對其實現感到好奇,則源代碼位於標準庫中的_threading_local.py中。

0

就像所有其他語言一樣,Python中的每個線程都可以訪問相同的變量。 「主線程」和子線程之間沒有區別。

與Python的一個區別是全局解釋器鎖意味着一次只有一個線程可以運行Python代碼。這對於同步訪問沒有多大幫助,但是,由於所有通常的先發制人問題仍然適用,並且必須像使用其他語言一樣使用線程原語。但是,這意味着如果您使用線程來獲得性能,則需要重新考慮。

13

您可以使用threading.local()創建線程本地存儲。

>>> tls = threading.local() 
>>> tls.x = 4 
>>> tls.x 
4 

存儲到TLS數據將是唯一的每個線程,這將有助於確保無意共享不會發生。

53

考慮下面的代碼:

#/usr/bin/env python 

from time import sleep 
from random import random 
from threading import Thread, local 

data = local() 

def bar(): 
    print("I'm called from", data.v) 

def foo(): 
    bar() 

class T(Thread): 
    def run(self): 
     sleep(random()) 
     data.v = self.getName() # Thread-1 and Thread-2 accordingly 
     sleep(1) 
     foo() 
 >> T().start(); T().start() 
I'm called from Thread-2 
I'm called from Thread-1

這裏threading.local()被用作一個快速和骯髒的方式來傳遞從run()的一些數據來禁止()不改變的接口FOO()。

注意,使用全局變量不會做的伎倆:

#/usr/bin/env python 

from time import sleep 
from random import random 
from threading import Thread 

def bar(): 
    global v 
    print("I'm called from", v) 

def foo(): 
    bar() 

class T(Thread): 
    def run(self): 
     global v 
     sleep(random()) 
     v = self.getName() # Thread-1 and Thread-2 accordingly 
     sleep(1) 
     foo() 
 >> T().start(); T().start() 
I'm called from Thread-2 
I'm called from Thread-2

同時,如果你有能力通過通過這個數據爲Foo()的參數 - 這將是一個更優雅和精心設計的方式:

from threading import Thread 

def bar(v): 
    print("I'm called from", v) 

def foo(v): 
    bar(v) 

class T(Thread): 
    def run(self): 
     foo(self.getName()) 

但是,這並不總是可能的時使用第三方或設計不佳的代碼。

相關問題