上一篇文章提到多线程好处,但多线程同时操作也造成了数据的不安全性,因此本篇文章引入了python锁机制。包括同步锁、死锁以及递归锁的相关操作。希望感兴趣的小伙伴可以坚持看下去同时欢迎提出宝贵的意见让我们一起进步!
01:锁机制的引入
案例说明:
小明有一张银行卡余额500元,这张银行卡绑定了小明妻子的微信和支付宝。
小明今天发工资收入人民币10000元,在银行汇款的同时小明的妻子通过支付宝消费口红一支300元。
此时多个线程都在同时操作同一个共享资源,造成了资源破坏,怎么办呢?
解决方案:
如果把这些重要数据操作上锁同时只允许一个线程进行操作;
操作完毕后再把锁打开,如此就可以避免了数据安全的问题。
02:不安全的多线程并发
看看下面的代码执行后会得到几种答案呢?
import time,threading
account_balance=500
def option_num(num):
global account_balance
balance=account_balance
time.sleep(1)
balance+=num
account_balance=balance
t1=threading.Thread(target=option_num,args=(-300,))
t2=threading.Thread(target=option_num,args=(10000,))
t1.start()
t2.start()
t1.join()
t2.join()
print('账户余额为:',account_balance)#10500、200
03:互斥锁(Lock)
1)概述:线程调用Lock对象的acquire方法来获取锁对象(如果其他线程已经获得该锁,则当前线程需等待被释放),待资源访问完后再调用release方法释放锁。
2)特点:Lock每次只能锁定一次,其余的锁请求,需等待锁释放后才能获取
3)注意:release使用前线程必须已获得锁定,否则将抛出异常
import time,threading
account_balance=500
r=threading.Lock()#一把锁
def option_num(num):
global account_balance
r.acquire()#上锁
balance=account_balance
time.sleep(1)
balance+=num
account_balance=balance
r.release()#解锁
t1=threading.Thread(target=option_num,args=(-300,))
t2=threading.Thread(target=option_num,args=(10000,))
t1.start()
t2.start()
t1.join()
t2.join()
print('账户余额为:',account_balance)#10200
04:死锁
死锁:在线程间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,就会造成死锁。因为系统判断这部分资源都正在使用,所有这两个线程在无外力作用下将一直等待下去。
小明今天去面试,面试官说:“你给我解释一下什么是死锁,我就给你发 offer”。
小明一听就不乐意了,心想你还挺横,于是就说:“你给我发 offer 我就告诉你什么叫死锁。”
诺!这就是死锁,小明和面试官都霸占有对方的资源不肯松手并且同时在等待对方的资源。
import threading,time
lockA=threading.Lock()
lockB=threading.Lock()
#面试官
def foo1():
lockA.acquire()#获取公共锁,如果锁被占用,则阻塞在这里,等待锁的释放
print('请解释死锁现象')
time.sleep(1)
lockB.acquire()
print('请发Offer')
time.sleep(1)
lockB.release()#释放公共锁
print('foo1资源释放B锁')
lockA.release()
print('foo1资源释放A锁')
#小明
def foo2():
lockB.acquire()#获取公共锁,如果锁被占用,则阻塞在这里,等待锁的释放
print('等待Offer')
time.sleep(1)
lockA.acquire()
print('开始解释死锁')
time.sleep(1)
lockA.release()#释放公共锁
print('foo2资源释放B锁')
lockB.release()
print('foo2资源释放A锁')
t1=threading.Thread(target=foo1)
t2=threading.Thread(target=foo2)
t1.start()
t2.start()
t1.join()
t2.join()
05:递归锁(Reentrant Lock)
1)递归锁概述:代表可重入锁,在同一个线程中可以对它进行多次锁定,也可以多次释放
2)原理:RLock 内部维护着一个 Lock 和一个 counter 变量,counter 记录了 acquire 的次数,从而使得资源可以被多次 acquire。直到一个线程所有的 acquire 都被 release,其他的线程才能获得资源。
3)注意:如果使用 RLock,那么 acquire() 和 release() 方法必须成对出现。如果调用了 n 次 acquire() 加锁,则必须调用 n 次 release() 才能释放锁。
import threading,time
lockR=threading.RLock()#递归锁
def foo1():
lockR.acquire()
print('请解释死锁现象')
time.sleep(1)
lockR.acquire()
print('请发Offer')
lockR.release()
print('foo1资源释放B锁')
lockR.release()
print('foo1资源释放A锁')
def foo2():
lockR.acquire()
print('等待Offer')
time.sleep(1)
lockR.acquire()
print('开始解释死锁')
lockR.release()
print('foo2资源释放B锁')
lockR.release()
print('foo2资源释放A锁')
t1=threading.Thread(target=foo1)
t2=threading.Thread(target=foo2)
t1.start()
t2.start()
t1.join()
t2.join()
网友评论