11. 线程本地变量ThreadLocal的使用

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内部会处理

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

@十三阿哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值