这次让我们来看看一个真实场景吧:银行转账
一、举例银行转账
假设现在有一个xuewei的账号里面有 100W。
然后有多个任务在转账,转入转出都是跟这个xuewei账号相关的。
而且这些任务都是随机的。
我们先把上面的场景写成代码:
xuewei_account = 100
# amount为负数即是转出金额
def transfer(money):
global xuewei_account
xuewei_account = money
下面是多个线程,多线程模拟转账事件,我们假设有4个事件在同时发生。
import random
import threading
import datetime
import time
xuewei_account = 100
# amount为负数即是转出金额
def transfer(money):
global xuewei_account
xuewei_account = money
# 创建4个任务给学委账户转账
for i in range(10000):
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
# 等待活跃线程只剩下主线程MainThread
time.sleep(10)
print("-" * 16)
print("活跃线程数:", threading.active_count())
print("活跃线程:", threading.current_thread().name)
print("学委账户余额:", xuewei_account)
这里启动了4个线程循环了10000次,也就是4万个线程,分别于学委的账户进行转账。
下面是运行结果:
运行几次学委的账户还是正确的,余额还是100W。
上面的代码线程几万个,但每次运行的操作都很简单,完成一次加法。
线程一个接一个start,非常快速就切换下一个线程, 我们看到程序没有出现问题。
下面进行改造,这次不要就4万线程了,我们让转账这个任务耗时更多,每启动一个线程进行模拟10万次转账。
import random
import threading
import datetime
import time
xuewei_account = 100
# amount为负数即是转出金额
def transfer(money):
global xuewei_account
for x in range(100000):
xuewei_account = money
创建4个任务给重复学位账户转账:
for i in range(10):
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
time.sleep(10)
print("-" * 16)
print("活跃线程数:", threading.active_count())
print("活跃线程:", threading.current_thread().name)
print("学委账户余额:", xuewei_account)
这里运行的结果就比较出乎意料了:
多线程编程复杂的地方就在这里了, 有时候明明平平无奇的代码,改造成多线程,就很容易出bug!
当然上面的代码并不是平平无奇,相比第一段代码,上面的转账函数做的事件更多,更耗时。
二、问题解决
我们加上锁。
代码如下:
import random
import threading
import datetime
import time
xuewei_account = 100
lock = threading.Lock()
# amount为负数即是转出金额
def transfer(money):
lock.acquire()
global xuewei_account
for x in range(100000):
xuewei_account = money
lock.release()
# 创建4个任务给重复学委账户转账
for i in range(10):
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
threading.Thread(target=lambda: transfer(-1)).start()
threading.Thread(target=lambda: transfer(1)).start()
time.sleep(10)
print("-" * 16)
print("活跃线程数:", threading.active_count())
print("活跃线程:", threading.current_thread().name)
print("学委账户余额:", xuewei_account)
运行结果如下:
上面的代码不管怎么运行,运行多少次最后学委的账户都是100.(PS:学委不会联系读者转账的,这个特别注意)。
不管多少个线程,每次转账函数内部转账的代码(从global到 = money这一段代码)只会被一个线程调用。
三、总结
展示了通过同步机制解决一些编程问题的思路。读者可以多多借鉴,思考锁的应用。
为什么在对amount重度操作(本文第二段代码)的时候,计算就出错了!
并发编程是高度利用CPU计算能力的编程方式,并发程序也就是在并行执行同类任务的程序。这个可以跟单线程应用比较。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至123456@qq.com 举报,一经查实,本站将立刻删除。