在Python中,具体如何在线程之间共享变量?

虽然在我从未真正理解或看过如何共享变量的示例之前我已经使用过 threading.Thread 。它们是在主线和孩子之间共享还是仅在孩子之间共享?我什么时候需要使用线程本地存储来避免这种共享?

我已经看到很多关于通过使用锁来同步线程间共享数据访问的警告,但我还没有看到问题的一个很好的例子。

提前致谢!

有帮助吗?

解决方案

在Python中,除了函数局部变量之外,所有内容都是共享的(因为每个函数调用都有自己的本地集合,而线程总是单独的函数调用。)即便如此,只有变量本身(引用的名称)到对象)是函数的本地;对象本身总是全局的,任何东西都可以引用它们。 在这方面,特定线程的 Thread 对象不是特殊对象。如果将 Thread 对象存储在所有线程都可以访问的地方(如全局变量),那么所有线程都可以访问那个 Thread 对象。如果你想原子地修改你不仅仅在同一个线程中创建的任何,并且没有存储另一个线程可以获得它的任何地方,你必须通过锁来保护它。并且所有线程当然必须共享这个锁,否则它将不会非常有效。

如果你想要实际的线程局部存储,那就是 threading.local 的来源。 threading.local 的属性不在线程之间共享;每个线程只看到它自己放在那里的属性。如果您对它的实现感到好奇,请使用 _threading_local.py 在标准库中。

其他提示

请考虑以下代码:

#/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()传递给bar(),而无需更改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())

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

您可以使用 threading.local()创建线程本地存储。

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

存储到tls的数据对于每个线程都是唯一的,这将有助于确保不会发生无意的共享。

就像在所有其他语言中一样,Python中的每个线程都可以访问相同的变量。 “主线程”和子线程之间没有区别。

与Python的一个区别是全局解释器锁意味着一次只能有一个线程运行Python代码。然而,在同步访问时,这并没有多大帮助,因为所有常见的抢占问题仍然适用,并且您必须像其他语言一样使用线程原语。但这确实意味着你需要重新考虑是否使用线程来提高性能。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top