python-不安全的并发(同步锁、死锁)
一、为什么不安全
【1】假设a的银行账户有balance=500,此时公司像银行账户转入工资1000元,a此时又在淘宝上购买200元的商品。因为并发,如果此时银行获取balance=500,在银行系统内a的账户内就有1500元,此时该结果还没返回到a账户,这个时候淘宝也拿到了balance=500,并且减-200=300,返回到a账户内,此时balance=300;淘宝结算结束后,银行系统内的结果才返回到a账户,这个时候a账户内balance=1500
【2】不进行锁 ,则以下代码会造成输出的时候,有时候是200,有时候是1500
# -*- coding:utf-8 -*-
# __author__:pansy
# 2022/5/11
# 账户余额
import time,threading
account_balance = 500
def foo(num):
# 需要返回最终的account_balance,因为该变量是全局变量,在局部作用域内只能声明,不能修改;如果要修改必须声明为global
global account_balance
# 引用账户余额,赋值给balance(引用的时候不需要将account_balance声明为global)
balance = account_balance
time.sleep(1)
# balance变量要加上函数传进来的入参
balance = balance + num
# 返回最终的结果,此时修改了account_balance,所以要 global account_balance
account_balance = balance
# 创建线程
t1 = threading.Thread(target=foo,args=(1000,)) # 银行打钱进来
t2 = threading.Thread(target=foo,args=(-300,)) # 淘宝消费
# 启动线程
t1.start()
t2.start()
# 需要阻塞主线程,当t1和t2线程结束后,主线程再执行打印,然后再结束主线程
t1.join()
t2.join()
# 最终余额打印
print('最终余额为:%.2f' % account_balance) # 输出可能是200.00,可能是1500.00
二、同步锁
【1】既然不安全,那只需要把重要数据balance锁住,当线程1在使用balance的时候,把balance锁定只能给该线程用;线程2被挡住无法使用balance;当线程1使用完balance后,再放开balance,线程2调用balance同时再次将balance锁住
【2】创建对象threading.Lock()
# -*- coding:utf-8 -*-
# __author__:pansy
# 2022/5/11
# 账户余额
import time,threading
# 创建锁
r = threading.Lock() # 同步锁
account_balance = 500
def foo(num):
# 在操作重要数据前,先锁定
r.acquire()
# 需要返回最终的account_balance,因为该变量是全局变量,在局部作用域内只能声明,不能修改;如果要修改必须声明为global
global account_balance
# 引用账户余额,赋值给balance(引用的时候不需要将account_balance声明为global)
balance = account_balance
time.sleep(1)
# balance变量要加上函数传进来的入参
balance = balance + num
# 返回最终的结果,此时修改了account_balance,所以要 global account_balance
account_balance = balance
# 操作完重要数据后,解锁
r.release()
# 创建线程
t1 = threading.Thread(target=foo,args=(1000,)) # 银行打钱进来
t2 = threading.Thread(target=foo,args=(-300,)) # 淘宝消费
# 启动线程
t1.start()
t2.start()
# 需要阻塞主线程,当t1和t2线程结束后,主线程再执行打印,然后再结束主线程
t1.join()
t2.join()
# 最终余额打印
print('最终余额为:%.2f' % account_balance) # 最终输出始终为1200.00
【4】锁定只能锁一次,不能锁两次