Python中的“线程局部存储”是什么?我为什么需要它?
在Python中,变量是如何在多个线程之间共享的呢?
虽然我之前使用过threading.Thread
,但我从来没有真正理解过或者看到过变量是如何共享的。变量是主线程和子线程之间共享,还是只在子线程之间共享?我什么时候需要使用线程本地存储来避免这种共享呢?
我看到很多关于使用锁来同步线程之间共享数据访问的警告,但我还没有看到一个很好的例子来说明这个问题。
6 个回答
你可以使用 threading.local()
来创建线程本地存储。
>>> tls = threading.local()
>>> tls.x = 4
>>> tls.x
4
存储在这个线程本地存储里的数据对每个线程都是独一无二的,这样可以确保不会发生意外的数据共享。
考虑以下代码:
#/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())
但在使用第三方库或者设计不好的代码时,这种方法并不总是可行。
在Python中,除了函数内部的局部变量,其他的东西都是共享的。函数每次被调用时,都会有自己的一套局部变量,而线程之间的调用是相互独立的。即使是局部变量,只有变量名(也就是指向对象的名字)是函数内部的,真正的对象本身是全局的,任何地方都可以访问到它们。
对于特定的线程来说,Thread
对象并没有什么特别之处。如果你把这个Thread
对象存储在所有线程都能访问的地方(比如全局变量),那么所有线程都能访问到这个Thread
对象。如果你想要安全地修改其他线程可以访问的任何东西,就必须用锁来保护它。而且所有线程当然要共享同一个锁,不然就没什么效果了。
如果你想要真正的线程本地存储,这时候就需要用到threading.local
。threading.local
的属性在不同线程之间是不会共享的;每个线程只能看到自己放进去的属性。如果你对它的实现感兴趣,可以在标准库的_threading_local.py文件中找到源代码。