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
猜你喜欢
- 前段时间在开发雨哲树网程序的时候,遇到需要转换地址中的参数,需要用到简单可逆运算的加密功能。在网上找了很多都不理想。因为我需要的这个可逆运算
- 因为Python是自带文档,可以通过help函数来查询每一个系统函数的用法解释说明。一般来说,关键的使用方法和注意点在这个系统的文档中都说的
- 如何制作一个防止多次刷新计数的图片计数器?请问如何做一个专业的图片计数器? <%countlong
- 随着网络的发展,网速和机器速度的提高,越来越多的网站用到了丰富客户端技术。而现在Ajax则是最为流行的一种方式。JavaScript是一种解
- 1、终极方法:条件注释<!--[if lte IE 6]> 这段文字仅显示在 IE6及IE6以下版本。 <![endif]
- 1、有一个论坛,帖子的数据巨大,请简要说明如何提高用户搜索帖子的效率。 在程序方面,可以使用页面缓存技术。在前台界面着设计方面也可以让用户输
- 放到公用调用文件(如conn数据库链接文件),对所有GET或POST的数据进行过滤特殊字符串,以实现简单有效的SQL注入过滤Function
- 一空间多域名绑定3种方法,HTML代码格式:<html> <script language=javascript
- 原本运行正常的ASP页面,今天突然提示: 代码如下: Microsoft VBScript 运行时错误 错误 '800a01a8&
- 如下所示:#-*- encoding:utf-8 -*-import csvimport sys,osimport pymysql def
- PNG格式以支持透明和无损,且相对大小适中,已成为现在网页中图片运用的主流。有些时候我们在制作网页时使用PNG格式图片,用IE浏览器查看却无
- 本文实例分析了CI框架出现mysql数据库连接资源无法释放的解决方法。分享给大家供大家参考,具体如下:使用ci框架提供的类查询数据:$thi
- 支付宝lab的意思是支付宝实验室,也就是概念产品聚集地,可以让用户快速试用这些新产品。本次支付宝lab logo设计历时一个星期,视觉设计组
- Go pongo2 教程展示了如何使用 pongo2 模板引擎在 Golang 中使用模板。模板引擎是一个库,旨在将模板与数据结合起来以生成
- python微信跳一跳,前言 这是python玩跳一跳系列博文中一篇,主要内容是用颜色识别的方法来进行跳跳小人的定位。颜色识别通过观察,我们
- 尽管 JavaScript 历史上使用冗长而令人生厌的代码块来标的特定浏览器的时期已经结束了,但是偶尔使用一些简单的代码块和对象检测来确保一
- Access保留字&变量名列表,建表时应避免使用这些词汇和符号。Access 2002/2003-A &nbs
- 我们一般采用photoshop等做图工具制作电视扫描线效果图片:首先做一个黑白相间的图案,然后用这个图案进行填充,再调整图层的模式或者透明度
- 在MySQL中,对于索引的使用并是一直都采用正确的决定。简单表的示例:CREATE TABLE `r2` (ID` int(11) DEFA
- 了兑现我对大家的承诺,我们现在立即就将“借助数据库和ASP程序”编写出来的,可以同时适用于IIS和P