TLS
线程局部存储 TLS (Thread Local Storage)是一种多线程编程中的机制,它实现了在每个线程中独立存储一份数据的拷贝,使线程在访问特定数据时互不干扰,避免了多线程场景下的数据干扰和竞争。
其实现大概可以抽象成一个储物柜,在 Python 中通过 threading.local()
创建这个存储柜,每个线程相当于柜子的使用者,每个线程持有其中一个柜子,柜子里存放的是该线程独有的数据拷贝,这样就可以避免和其他线程的数据混淆。
一个简单的 example:
import time
import threading
from concurrent.futures import ThreadPoolExecutor
userName = threading.local()
def SessionThread(userName_in):
time.sleep(0.5)
t = threading.currentThread()
print('Thread id : %d' % t.ident)
print('Thread name : %s' % t.getName())
userName.val = userName_in
print('User name : %s' % userName.val)
pool = ThreadPoolExecutor()
for name in ['User1', 'User2']:
pool.submit(SessionThread, name)
输出结果为:
Thread id : 123145562861568
Thread name : ThreadPoolExecutor-1_0
User name : User1
Thread id : 123145529282560
Thread name : ThreadPoolExecutor-1_1
User name : User2
在实际的项目应用中可以通过 TLS 实现一个参数透传的功能,使某些参数在不同模块的方法间的调用中不需要层层传递,更进一步地,可以通过 with 创建context,使得 with 内的代码块所调用到的函数都持有某一参数,具体可以参考 Towhee 中的param_scope
实现。
ContextVar
TLS 实现的是多线程间的隔离,作用范围是一个线程的执行上下文,但如果是在异步的场景下可能会存在问题。因此我们需要实现协程内的隔离,Python 在3.7以后的版本提供了 contextvars 模块,用于创建 ContextVar 作用于一个异步任务的执行上下文,可以在多个协程中共享上下文变量,每个协程持有一份拷贝,不会相互干扰。
其用法与 TLS 类似,只是通过 set 和 get 赋值和获取:
import time
import contextvars
from concurrent.futures import ThreadPoolExecutor
username = contextvars.ContextVar("username", default='user')
def SessionThread(userName_in = None):
time.sleep(0.1)
t = threading.currentThread()
print('Thread id : %d' % t.ident)
print('Thread name : %s' % t.getName())
if userName_in:
username.set(userName_in)
print('User name : %s' % username.get())
pool = ThreadPoolExecutor()
pool.submit(SessionThread)
for name in ['User1', 'User2']:
pool.submit(SessionThread, name)
输出为:
Thread id : 123145531932672
Thread name : ThreadPoolExecutor-6_0
User name : user
Thread id : 123145498353664
Thread name : ThreadPoolExecutor-6_1
User name : User1
Thread id : 123145515143168
Thread name : ThreadPoolExecutor-6_2
User name : User2