Python线程之如何解决共享变量问题
作者:雷学委 发布时间:2023-08-27 16:15:56
前面提到了银行转账这个场景,展示了一个比较耗时的转账操作。
这篇继续转帐,下面展示一段程序,多个线程的操作都更改了amount
变量导致运行结果不对的问题。
前文说了转账问题
下面展示另一种转账的方式:
import random
import threading
import datetime
import time
xuewei = {'balance': 157}
# amount为负数即是转出金额
def transfer(money):
name = threading.current_thread().getName()
print("%s 给xuewei转账 %s " % (name, money))
xuewei['balance'] += money
print("xuewei账户余额:", xuewei['balance'])
lists = [-7, 20, -20, 7] # 4次转账的数额,负数为学委的账户转出,正数为他人转入。
# 创建4个任务给学委转账上面lists的金额
threads = []
for i in range(4):
amount = lists[i]
name = "t-" + str(i)
print("%s 计划转账 %s" % (name, amount))
mythread = threading.Thread(name=name, target=lambda: transfer(amount))
threads.append(mythread)
# 开始转账
for t in threads:
t.start()
# 等待3秒让上面的转账任务都完成,我们在看看账户余额
time.sleep(3)
print("-" * 16)
print("学委账户余额:", xuewei['balance'])
这里启动了4个线程,每个线程内有个lambda
表达式,分别于学委的账户进行转账,但是最后结果是185. 而不是157.
下面是运行结果:
PS: 这只是一种运行结果。多线程的运行结果不是永远一样的。
如何解决这个问题?
观测结果我们发先amount
只保留了最后一个值。
好,下面改造一下:
import random
import threading
import datetime
import time
xuewei = {'balance': 157}
lists = [-7, 20, -20, 7] # 4次转账的数额,负数为学委的账户转出,正数为他人转入。
def transfer(amount):
name = threading.current_thread().getName()
print("%s 给xuewei转账 %s " % (name,amount))
xuewei['balance'] += amount
print("xuewei账户余额:", xuewei['balance'])
# 创建4个任务给学委转账上面lists的金额
for i in range(4):
amount = lists[i]
name = str(i)
# mythread = threading.Thread(name=name, target=lambda: transfer(amount))
def event():
print("%s 计划转账 %s" % (name, amount))
transfer(amount)
mythread = threading.Thread(name=name, target=event)
mythread.start()
# 等待3秒让上面的转账任务都完成,我们在看看账户余额
time.sleep(3)
print("-" * 16)
print("学委账户余额:", xuewei['balance'])
学委这里加了一个event
函数,把转账计划打印出来。
从下面的一次运行结果看,event函数的输出结果没错,所有”计划转账“金额都如预期[-7, 20, -20 7]。 问题是transfer函数再多线程执行的时候,我们发现amount被多线程竞争修改了:
用户0转账金额变成20
用户1转账金额变成-20
用户2转账金额变成7
用户3转账金额变成7
也就是说,amount
被后面的线程修改了,但是前面线程还没有执行完。
用户0应该转账-7的,中间还没有执行完毕,结果被线程1修改了amount为20,用户0继续执行转账,余额变成177. 其他依次推理。
amount
这个变量被多个线程竞争修改了,这个就是程序的共享变量。
到底如何解决?
方法非常简单:直接干掉共享变量。
下面就是消除共享变量的方法: 让共享变成每个线程访问独立运行空间
所以代码改动如下:
import random
import threading
import datetime
import time
xuewei = {'balance': 157}
lists = [-7, 20, -20, 7] # 4次转账的数额,负数为学委的账户转出,正数为他人转入。
# 我们不要依赖amount变量了
def transfer():
name = threading.current_thread().getName()
xuewei['balance'] += lists[int(name)] #通过线程名字来获取对应金额
print("xuewei账户余额:", xuewei['balance'])
# 创建4个任务给学委转账上面lists的金额
threads = []
for i in range(4):
amount = lists[i]
name = str(i)
print("%s 计划转账 %s" % (name, amount))
# mythread = threading.Thread(name=name, target=lambda: transfer())
def event():
transfer()
mythread = threading.Thread(name=name, target=event)
threads.append(mythread)
# 开始转账
for t in threads:
t.start()
# 等待3秒让上面的转账任务都完成,我们在看看账户余额
time.sleep(3)
print("-" * 16)
print("学委账户余额:", xuewei['balance'])
运行结果如下:
上面的代码不管怎么运行,运行多少次最后学委的账户都是157.
这次展示的另一种方式来避开多线程出现bug的方法,使用一个list下标跟线程名字一一对应,这样只要是对应名字的线程拿到的数值不错错乱。
来源:https://levin.blog.csdn.net/article/details/121965391


猜你喜欢
- 1.虚拟机数据库设置--重启数据库(1)vi /etc/my.cnfbind = 0.0.0.0(2).远程不能用root用户连,得新建用户
- 网络爬虫抓取特定网站网页的html数据,但是一个网站有上千上万条数据,我们不可能知道网站网页的url地址,所以,要有个技巧去抓取网站的所有h
- 在Microsoft SQL Server 2000中,用于数据存储的实用工具是数据库。数据库的物理表现是操作系统文件,即在物理上,一个数据
- 先来看javascript的直接写在了input上 <input name="pwuser" type="
- 在《javascript设计模式》中对这种方法作了比较详细的描述,实现方法的链式调用,只须让在原型中定义的方法都返回调用这些方法的实例对象的
- 前言最近在写 echarts 的时候碰到了这么一个报错,如下图。造成报错的原因是因为 echarts 的图形容器还未生成就对其进行了初始化,
- 1、把这段拷到DW里,存成HTML文件。<HTML><HEAD><TITLE>move backgrou
- 前言:对于Web开发人员,弹出框和提示框的使用肯定不会陌生,比如常见的表格新增和编辑功能,一般常见的主要有两种处理方式:行内编辑和弹出框编辑
- PL/SQL是ORACLE对标准数据库语言的扩展,ORACLE公司已经将PL/SQL整合到ORACLE 服务器和其他工具中了,近几年中更多的
- 上次我重新修改了UBB的转换后,又很多朋友反映日文显示的时候出错了。我在本地测试了一下,结果出现了 Invalid procedure ca
- 1、安装mysql5.7CentOS 7之后的版本yum的默认源中使用MariaDB替代原先MySQL,因此安装方式较为以往有一些改变:下载
- 我们在平常的系统开发中常常会遇到像无限级分类这样的树型结构数据,现提供一个可用的数据库存储过程,可以完成树型结构数据的排序。环境:windo
- 这个类可以用来搜索在给定的文本目录中的文件。 它可以给定目录遍历递归查找某些文件扩展名的文件。 并打开找到的文件,并检查他们是否包含搜索词语
- 本文实例讲述了Python实现读取txt文件并转换为excel的方法。分享给大家供大家参考,具体如下:这里的txt文件内容格式为:892天平
- 本案例将学习运用LyScript计算特定程序 * 定某些片段的Hash特征值,并通过xlsxwriter这个第三方模块将计算到的hash值存储
- # 查看下centos7.6上的python版本[root@registry ~]# cat /etc/redhat-releaseLinu
- 这篇文章主要介绍了简单了解Python读取大文件代码实例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的
- JOSN字符串转换为自定义类实例对象有时候我们有这种需求就是把一个JSON字符串转换为一个具体的Python类的实例,比如你接收到这样一个J
- 今天在intellij调试spark的时候感觉每次有新的一段代码,都要重新跑一遍,如果用spark-shell,感觉也不是特别方便,如果能像
- 本文较为详细的讲述了Python中常用的模块,分享给大家便于大家查阅参考之用。具体如下:1.内置模块(不用import就可以直接使用)常用内