2008-09-19 77 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()) 

但是,这并不总是可能的时使用第三方或设计不佳的代码。

相关问题