1. 对ThreadLocal的理解
ThreadLocal,有人称它为线程本地变量
,也有人称它为线程本地存储
,其实表达的意思是一样的。ThreadLocal在每一个变量中都会创建一个副本,每个线程都可以访问自己内部的副本变量
。
在多线程环境下,每个线程都有自己的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能看见,不会影响其他线程
,而全局变量的修改必须加锁。
2. 为什么会出现ThreadLoal这种技术应用
我们知道多线程环境下,每一个线程均可以使用所属进程的全局变量。如果一个线程对全局变量进行了修改,将会影响到其他所有的线程对全局变量的计算操作,从而出现数据混乱
,称之为脏数据
。为了避免多个线程同时对变量进行修改,引入了线程同步进制,通过互斥锁、条件变量、信号量或读写锁这种手段来控制对全局变量的访问。
只用全局变量并不能满足多线程环境的需求,很多时候线程还需要拥有自己的私有数据,这些数据对于其他线程来说是不可见的
。因此线程中也可以使用局部变量,局部变量只有线程自身可以访问,同一个进程下的其他线程不可访问。
有时候使用局部变量很不方便,为此python中提供了ThreadLocal变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的
。
ThreadLocal真正的做到了线程之间的数据隔离
。
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等
,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。
3. ThreadLocal的雏形
# demo
# 我们开启2个线程, 分别是线程1和线程2, 并且每个线程携带一个Student对象,
# 当执行线程函数的时候, 将线程名与线程参数(Student对象)依据线程名存储在g_dict中,
# 然后通过线程名来执行不同的操作do_1()和do_2(),
# 注意在do_1()和do_2()中我们并没有传入参数, 而是根据线程名去寻找与之绑定的数据,
# 这样一样, 确实在不同的线程之中起到了保存各自数据的一种效果.
import threading
g_dict = {}
class Student:
def __init__(self, num, name):
self.num = num
self.name = name
def __str__(self):
return '{0}:{1}'.format(self.num, self.name)
def do_1():
s = g_dict[threading.current_thread().name]
print('%s in %s' % (s, threading.current_thread().name))
def do_2():
s = g_dict[threading.current_thread().name]
print('%s in %s' % (s, threading.current_thread().name))
def MyThread(student):
g_dict[threading.current_thread().name] = student
if threading.current_thread().name == 'thread_1':
do_1()
elif threading.current_thread().name == 'thread_2':
do_2()
else:
pass
t1 = threading.Thread(target=MyThread, args=(Student(101, 'Zhang'),), name="thread_1")
t2 = threading.Thread(target=MyThread, args=(Student(102, 'Wang'),), name="thread_2")
t1.start()
t2.start()
t1.join()
t2.join()
4. ThreadLocal的初步使用
import threading
# 创建全局的线程本地变量
local_var = threading.local()
class Student:
def __init__(self, num, name):
self.num = num
self.name = name
def __str__(self):
return '{0}:{1}'.format(self.num, self.name)
class Score:
def __init__(self, score):
self.score = score
def __str__(self):
return '{0}'.format(self.score)
def do():
stu = local_var.student # 获取当前线程关联的Student对象
sco = local_var.score # 获取当前线程关联的Score对象
print('%s is %s in [%s]' % (stu, sco, threading.current_thread().name))
# 将stu和sco变量存储在local_var下去,
# 可以这么理解, 当线程1执行时, 将local_var进行了一份拷贝, 然后将线程1相关的数据保存在其中,
# 线程2亦是如此...
# 当执行do()时, local_var会自动根据当前线程拿出与其所绑定的数据...
def MyThread(stu, sco):
local_var.student = stu
local_var.score = sco
do()
t1 = threading.Thread(target=MyThread, args=(Student(101, 'Zhang'), Score(100)), name="thread_1")
t2 = threading.Thread(target=MyThread, args=(Student(102, 'Wang'), Score(98)), name="thread_2")
t1.start()
t2.start()
t1.join()
t2.join()
全局变量local_var就是一个ThreadLocal对象,每个Thread对它都可以读写student属性和score属性
,且互不影响
。你可以把local_var看成全局变量,但每个属性如local_var.student,local_var.score都是线程的局部变量,可以任意读写而互不干扰,也不用管理锁的问题,ThreadLocal内部会处理
。