文章详情

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

请输入下面的图形验证码

提交验证

短信预约提醒成功

Python线程安全实例分析

2023-06-29 07:31

关注

这篇文章主要介绍“Python线程安全实例分析”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Python线程安全实例分析”文章能帮助大家解决问题。

一、什么是线程安全?

线程安全,名字就非常直接,在多线程情况下是安全的,多线程操作上的安全。

比如一个计算加法的函数,不管是一千个还是一万个线程,我们希望它执行的结果总是正确的,1+1 必须永远等于2, 而不是线程少的时候1+1 变成3或者4了。

通常我们都用线程安全来修饰一个类,修饰一个函数:

我们会说我设计的这个类是线程安全的
这意味着,在多线程环境下,同时调用这个类的函数不会出现函数设置预期之外的异常(上述的1+1=3的情况)

二、在Python中有哪些类是线程安全的?

dict 和 list,tuple这些都是线程安全。

它们是被全局解释器保障了,这个锁:GIL(全局解释器锁)确保了任何时候只能有一个线程执行相应操作的字节码。参考

Python线程安全实例分析

但是这番话也是说的不清不楚的。

现在我们拿转账来解析吧:

xuewei_account = dict()xuewei_account['amount'] = 100# amount为负数即是转出金额def transfer(money):    xuewei_account['amount'] +=  money

如上,代码为一个函数对jb_account(账户)进行转入金额操作。

这里用了dict类型,GIL会保证只有一个线程操作账户。

下面是多个线程进行操作的代码:

import randomimport threadingimport datetimeimport timexuewei_account = dict()xuewei_account['amount'] = 100# amount为负数即是转出金额def transfer(money):    xuewei_account['amount'] +=  money# 创建4个任务给重复学委账户转账threads = []for i in range(200):    t1 = threading.Thread(target=lambda: transfer(-1))    threads.append(t1)    t2 = threading.Thread(target=lambda: transfer(1))    threads.append(t2)for t in threads:    t.start()    # 这次不用sleep了,用join来等待所有线程执行完毕# join函数必须线程start后才能调用,否则出错。for t in threads:    t.join()print("-" * 16)print("活跃线程数:", threading.active_count())print("活跃线程:", threading.current_thread().name)print("学委账户余额:", xuewei_account)

这段代码运行的输出结果正常,因为是反复+1/-1,最后肯定是恢复原账户余额。

虽然多个线程,但是每个线程只对xuewei_account进行一次读写,这时候dict是安全的。

Python线程安全实例分析

但是我们把赋值修改dict的操作变多之后(特别是一个线程内反复多次获取值然后修改),像下面的代码:

import randomimport threadingimport datetimeimport timexuewei_account = dict()xuewei_account['amount'] = 100# amount为负数即是转出金额def transfer(money):    for i in range(100000):        xuewei_account['amount'] = xuewei_account['amount'] + money# 创建400个任务重复给学委账户转账threads = []for i in range(200):    t1 = threading.Thread(target=lambda: transfer(-1))    threads.append(t1)    t2 = threading.Thread(target=lambda: transfer(1))    threads.append(t2)for t in threads:    t.start()for t in threads:    t.join()print("-" * 16)print("活跃线程数:", threading.active_count())print("活跃线程:", threading.current_thread().name)print("学委账户余额:", xuewei_account)

这是某一次运行结果(不保证每次acount的数值一样):

Python线程安全实例分析

我们看到dict还是扛不住多个线程反复的写操作。

这里区别是:每个线程只对xuewei_account进行大量读写,虽然dict是安全的,但是多个线程中间穿插修改了account,程序方法栈出现操作到旧值(看下面的图)。

主要是下面这段代码:

xuewei_account[‘amount'] += money # 即是 xuewei_account[‘amount'] = xuewei_account[‘amount']+ money

再一步抽象简化可以写成:

a = a + b

每个线程都执行 +b 操作,最后a的值应该是a+2b。

上面的操作意味这下面的情况发生了:

Python线程安全实例分析

在某个线程中可能出现某一个线程T1获取了a值 ,准备加上b。

另外一个线程T2已经完成了a+b操作,把a的值变成了a+b了。

但是接下来T1 拿了a的值再执行a+b操作,把a的值变成a+b。

这样就少加了一个b,本来最后结果是a+2b 的变成了 a+b(因为T1拿了a的旧值,中间T2执行完,T1才继续执行)

当然实际多线程之间交互比上图还要随机。

三、如何做到真正线程安全?

dict读取数据是线程安全,但是被反复读写就容易出现数据混乱。

如果我们要设计一个线程安全的函数,那么它必须不涉及任何共享变量或者是完全没有状态依赖的函数

def thread_safe_method():    pass

1.无状态函数

比如下面的加法函数,不管多少个线程调用,返回值永远是预期的a+b。

def add(a, b):    return a + b

2.另一种 化繁为简

许我们可以把多线程转换为单线程,这个需要一个线程安全的媒介。

关于“Python线程安全实例分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网行业资讯频道,小编每天都会为大家更新不同的知识点。

阅读原文内容投诉

免责声明:

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

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

软考中级精品资料免费领

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

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

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

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

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

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

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