文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

怎么理解Python中的ThreadLocal变量

2024-04-02 19:55

关注

这篇文章主要讲解了“怎么理解Python中的ThreadLocal变量”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解Python中的ThreadLocal变量”吧!

只用全局变量并不能满足多线程环境的需求,很多时候线程还需要拥有自己的私有数据,这些数据对于其他线程来说不可见。因此线程中也可以使用局部变量,局部变量只有线程自身可以访问,同一个进程下的其他线程不可访问。

有时候使用局部变量不太方便,因此 python 还提供了 ThreadLocal  变量,它本身是一个全局变量,但是每个线程却可以利用它来保存属于自己的私有数据,这些私有数据对其他线程也是不可见的。下图给出了线程中这几种变量的存在情况:

怎么理解Python中的ThreadLocal变量

线程变量

全局 VS 局部变量

首先借助一个小程序来看看多线程环境下全局变量的同步问题。

import threading global_num = 0 def thread_cal():     global global_num     for i in xrange(1000):         global_num += 1 # Get 10 threads, run them and wait them all finished. threads = [] for i in range(10):     threads.append(threading.Thread(target=thread_cal))     threads[i].start() for i in range(10):     threads[i].join() # Value of global variable can be confused. print global_num

这里我们创建了10个线程,每个线程均对全局变量 global_num  进行1000次的加1操作(循环1000次加1是为了延长单个线程执行时间,使线程执行时被中断切换),当10个线程执行完毕时,全局变量的值是多少呢?答案是不确定。简单来说是因为  global_num += 1 并不是一个原子操作,因此执行过程可能被其他线程中断,导致其他线程读到一个脏值。以两个线程执行 +1  为例,其中一个可能的执行序列如下(此情况下***结果为1):

怎么理解Python中的ThreadLocal变量

多线程全局变量同步

多线程中使用全局变量时普遍存在这个问题,解决办法也很简单,可以使用互斥锁、条件变量或者是读写锁。下面考虑用互斥锁来解决上面代码的问题,只需要在进行 +1  运算前加锁,运算完毕释放锁即可,这样就可以保证运算的原子性。

l = threading.Lock() ...     l.acquire()     global_num += 1     l.release()

在线程中使用局部变量则不存在这个问题,因为每个线程的局部变量不能被其他线程访问。下面我们用10个线程分别对各自的局部变量进行1000次加1操作,每个线程结束时打印一共执行的操作次数(每个线程均为1000):

def show(num):     print threading.current_thread().getName(), num def thread_cal():     local_num = 0     for _ in xrange(1000):         local_num += 1     show(local_num) threads = [] for i in range(10):     threads.append(threading.Thread(target=thread_cal))     threads[i].start()

可以看出这里每个线程都有自己的 local_num,各个线程之间互不干涉。

Thread-local 对象

上面程序中我们需要给 show 函数传递 local_num  局部变量,并没有什么不妥。不过考虑在实际生产环境中,我们可能会调用很多函数,每个函数都需要很多局部变量,这时候用传递参数的方法会很不友好。

为了解决这个问题,一个直观的的方法就是建立一个全局字典,保存进程 ID 到该进程局部变量的映射关系,运行中的线程可以根据自己的 ID  来获取本身拥有的数据。这样,就可以避免在函数调用中传递参数,如下示例:

global_data = {} def show():     cur_thread = threading.current_thread()     print cur_thread.getName(), global_data[cur_thread] def thread_cal():     cur_thread = threading.current_thread()     global_data[cur_thread] = 0     for _ in xrange(1000):         global_data[cur_thread] += 1     show()  # Need no local variable.  Looks good. ...

保存一个全局字典,然后将线程标识符作为key,相应线程的局部数据作为  value,这种做法并不***。首先,每个函数在需要线程局部数据时,都需要先取得自己的线程ID,略显繁琐。更糟糕的是,这里并没有真正做到线程之间数据的隔离,因为每个线程都可以读取到全局的字典,每个线程都可以对字典内容进行更改。

为了更好解决这个问题,python 线程库实现了 ThreadLocal 变量(很多语言都有类似的实现,比如Java)。ThreadLocal  真正做到了线程之间的数据隔离,并且使用时不需要手动获取自己的线程 ID,如下示例:

global_data = threading.local() def show():     print threading.current_thread().getName(), global_data.num def thread_cal():     global_data.num = 0     for _ in xrange(1000):         global_data.num += 1     show() threads = [] ... print "Main thread: ", global_data.__dict__ # {}

上面示例中每个线程都可以通过 global_data.num 获得自己独有的数据,并且每个线程读取到的 global_data  都不同,真正做到线程之间的隔离。

感谢各位的阅读,以上就是“怎么理解Python中的ThreadLocal变量”的内容了,经过本文的学习后,相信大家对怎么理解Python中的ThreadLocal变量这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯