首页 文章

什么是Python中的“线程本地存储”,为什么需要它?

提问于
浏览
78

特别是在Python中,如何在线程之间共享变量?

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

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

提前致谢!

4 回答

  • 1

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

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

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

  • 61

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

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

  • 63

    请考虑以下代码:

    #/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())
    

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

  • 15

    在Python中,除了函数局部变量之外,所有内容都是共享的(因为每个函数调用都有自己的本地集合,而线程总是单独的函数调用 . )即便如此,只有变量本身(引用对象的名称)是功能的本地;对象本身总是全局的,任何东西都可以引用它们 . 在这方面,特定线程的 Thread 对象不是特殊对象 . 如果将 Thread 对象存储在所有线程都可以访问的地方(如全局变量),则所有线程都可以访问该对象的 Thread 对象 . 如果你想以原子方式修改你所做的任何事情,那么它就不会有效 .

    如果你想要实际的线程本地存储,那就是 threading.local 所在的位置 . 在线程之间不共享 threading.local 的属性;每个线程只看到它自己放在那里的属性 . 如果您对它的实现感到好奇,那么源代码位于标准库的 _threading_local.py 中 .

相关问题