Python中的“线程局部存储”是什么?我为什么需要它?

132 投票
6 回答
81300 浏览
提问于 2025-04-11 09:18

在Python中,变量是如何在多个线程之间共享的呢?

虽然我之前使用过threading.Thread,但我从来没有真正理解过或者看到过变量是如何共享的。变量是主线程和子线程之间共享,还是只在子线程之间共享?我什么时候需要使用线程本地存储来避免这种共享呢?

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

6 个回答

29

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

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

存储在这个线程本地存储里的数据对每个线程都是独一无二的,这样可以确保不会发生意外的数据共享。

97

考虑以下代码:

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

但在使用第三方库或者设计不好的代码时,这种方法并不总是可行。

118

在Python中,除了函数内部的局部变量,其他的东西都是共享的。函数每次被调用时,都会有自己的一套局部变量,而线程之间的调用是相互独立的。即使是局部变量,只有变量名(也就是指向对象的名字)是函数内部的,真正的对象本身是全局的,任何地方都可以访问到它们。

对于特定的线程来说,Thread对象并没有什么特别之处。如果你把这个Thread对象存储在所有线程都能访问的地方(比如全局变量),那么所有线程都能访问到这个Thread对象。如果你想要安全地修改其他线程可以访问的任何东西,就必须用锁来保护它。而且所有线程当然要共享同一个锁,不然就没什么效果了。

如果你想要真正的线程本地存储,这时候就需要用到threading.localthreading.local的属性在不同线程之间是不会共享的;每个线程只能看到自己放进去的属性。如果你对它的实现感兴趣,可以在标准库的_threading_local.py文件中找到源代码。

撰写回答