千家信息网

Python线程安全实例分析

发表于:2025-02-20 作者:千家信息网编辑
千家信息网最后更新 2025年02月20日,这篇文章主要介绍"Python线程安全实例分析"的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇"Python线程安全实例分析"文章能帮助大家解决问题。一、什么是线
千家信息网最后更新 2025年02月20日Python线程安全实例分析

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

一、什么是线程安全?

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

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

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

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

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

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

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

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

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

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是安全的。

但是我们把赋值修改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的数值一样):

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

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

主要是下面这段代码:

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

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

a = a + b

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

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

在某个线程中可能出现某一个线程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线程安全实例分析"的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注行业资讯频道,小编每天都会为大家更新不同的知识点。

线程 安全 函数 账户 代码 面的 多个 结果 金额 实例 实例分析 分析 余额 情况 知识 负数 转账 任务 全局 加法 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 apex服务器不同步还无法重连 无线网络访问服务器异常 管理客户权限数据库系统 乐清存储服务器 数据库可以重复建相同的表吗 数据库删除表的某一行 高中学历 软件开发 数据库安全与备份 常用网络安全技术8大类 专业软件开发设施品质保障 魔兽世界提示连接不上服务器 网络安全宣传视频创意短片 银行 黄浦区新型网络技术答疑解惑 吃鸡游戏服务器要多少钱 2020公需科目网络安全 苹果电脑如何使用微软数据库 互联网科技自媒体灵感 网络安全具备 鞍山教育网服务器登录不上去 奥丁神叛台服服务器故 一级系统网络安全域 多台服务器怎样连接在一起 游戏服务器程序猿面试 信息化建设与网络安全三同步 瀚高数据库查看系统日志 连接路由器服务器无响应 数据库四个事务原则 浙江互联网智慧科技企业 宿舍数据库中应创建哪几个数据表 赛尔号服务器开放时间
0