第三阶段:day17_062919【threadlocal】
1.问题引入
在多线程环境下,每一个线程均可以使用所属进程的全局变量。如果一个线程对全局变量进行了修改,将会影响到其他所有的线程。为了避免多个线程同时对变量进行修改,引入了线程同步机制,可通过互斥锁来控制对全局变量的访问。
在多线程环境下,每个线程都有自己独立的数据。一个线程使用自己的局部变量比使用全局变量好,因为局部变量只有线程自己能访问,不会影响其他线程,而全局变量的修改必须加锁。但有时候使用局部变量也不太方便,比如调用其他函数时,参数的传递问题。
import threading
class Student:
def __init__(self,name):
self.name=name
def __str__(self):
return '<Student,%s>'%self.name
def deal_student(name):
print("线程【%s】执行任务,参数为:【%s】"%(threading.current_thread().getName(),name))
std = Student(name)
# std是局部变量,但是后续的每个功能函数都要用它,因此必须传进去
fun1(std)
fun2(std)
def fun1(std):
print("fun1接收到的参数为:",std,threading.current_thread().getName())
def fun2(std):
print("fun2接收到的参数为:",std,threading.current_thread().getName())
#测试
t1 = threading.Thread(target=deal_student,args=('莫莫',),name='momo')
t2 = threading.Thread(target=deal_student,args=('露露',),name='lulu')
t1.start()
t2.start()
- 每个函数一层一层调用都这么传参数那还得了?!用std做全局变量?也不行,因为每个线程处理不同的Student对象,std是不能共享的。
- 如果用一个全局dict(字典)存放所有的Student对象,然后以thread自身作为key获得线程对应的Student对象如何?可以
import threading
class Student:
def __init__(self,name):
self.name=name
def __str__(self):
return '<Student,%s>' % self.name
global_dict = {}
def deal_student(name):
print("线程【%s】执行任务,参数为:【%s】" % (threading.current_thread().getName(), name))
std = Student(name)
global global_dict
# 把std放到全局变量global_dict中
global_dict[threading.current_thread()] = std
fun1()
fun2()
def fun1():
# 不需要传入std,而是根据当前线程查找
global global_dict
std = global_dict[threading.current_thread()]
print("fun1",std)
def fun2():
# 任何函数都可以查找出当前线程的std变量
global global_dict
std = global_dict[threading.current_thread()]
print("fun2",std)
#测试
t1 = threading.Thread(target=deal_student,args=('莫莫',),name='momo')
t2 = threading.Thread(target=deal_student,args=('露露',),name='lulu')
t1.start()
t2.start()
2. ThreadLocal引入
上述的方案理论上是可行的,它最大的优点是消除了std对象在每层函数中的传递问题,但是每个函数获取std的代码有点丑。
有没有更简单的方式?ThreadLocal应运而生,不用查找全局的dict,ThreadLocal帮你自动做这件事。
python提供了ThreadLocal 变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的。
import threading
# 创建全局ThreadLocal对象:
threadLocal = threading.local()
class Student:
def __init__(self,name):
self.name=name
def __str__(self):
return '<Student,%s>' % self.name
def deal_student():
# 获取当前线程关联的student
std = threadLocal.student
print('Hello, %s (in %s)' % (std, threading.current_thread().name))
def deal_thread(name):
print("线程【%s】执行任务,参数为:【%s】" % (threading.current_thread().getName(), name))
# 当前线程绑定student对象到ThreadLocal
std = Student(name)
threadLocal.student = std # 给threadLocal绑定student属性
deal_student()
#测试
t1 = threading.Thread(target = deal_thread, args=('莫莫',), name = 'Thread-momo')
t2 = threading.Thread(target = deal_thread, args=('露露',), name = 'Thread-lulu')
t1.start()
t2.start()
3. ThreadLocal总结
-
全局变量threadLocal就是一个ThreadLocal对象,每个Thread对threadLocal变量都可以读写student属性,但互不影响。
-
一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。
-
ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题,真正做到了线程之间的数据隔离。
-
你可以把threadLocal看成全局变量,但每个属性如threadLocal.student都是线程的局部变量,可以任意读写而互不干扰,也不用考虑锁的问题,ThreadLocal内部会处理。
-
按课程步骤,你也可以理解全局变量threadLocal是一个dict,不但可以用threadLocal.student,还可以绑定其他变量,如threadLocal.teacher等等。
-
ThreadLocal最常用的地方就是为每个线程绑定一个数据库连接,HTTP请求,用户身份信息等,这样一个线程的所有调用到的处理函数都可以非常方便地访问这些资源。