Python3之并发(四)---线程锁
一、线程锁
保证多线程数据的一致性,对锁内的资源进行锁定,同一时间只能有一个线程来修改共享的数据
多个线程同时加了同一个锁对象时,先获取到锁的线程会继续运行,未获取到锁的线程会处于堵塞状态,直到前面的线程释放锁,重新获取到锁才会往下运行
类型
threading.Lock
基本锁对象,每次只能获取一次,其他锁请求需要等锁释放后才能获取
若想对同一线程多次锁,必须等前面的锁释放,否则会变成死锁
threading.RLock
可重入锁,同一个线程中可以多次获取,也可以多次释放,但是获取和释放的次数必须要一致
锁对象的方法
Lock.acquire(blocking=True, timeout=-1) 获取锁,获取成功返回 True,否则返回 False blocking: 默认True,堵塞没有获取到锁的线程 timeout: blocking=True时,没有获取到锁的线程的堵塞超时时间,默认-1,无限制等待;blocking=Flase时,禁止设置timeout Lock.release() 释放锁,可以从任何线程调用此函数,没有返回值 对已解锁的锁使用该方法引发 RuntimeError 异常
示例
不加锁时
取钱两次之后,余额是负数
import threading, time #账户类 class Account: def __init__(self, account_no, balance): #账户编号和账户余额 self.account_no = account_no self.balance = balancedef getBlance(self): return self.balance #提取现金方法 def draw(self, draw_amount): if self.balance >= draw_amount: print(threading.current_thread().name+'\t取钱成功!吐出钞票:'+str(draw_amount)) time.sleep(1) self.balance -= draw_amount print(threading.current_thread().name+'操作之后\t余额为:'+str(self.balance)) else: print(threading.current_thread().name+'\t取钱失败!余额不足!\t当前余额为:'+str(self.balance)) acct = Account('986623', 1500) thread_1 =threading.Thread(target=acct.draw, args=(800,), name='thread_1') thread_2 =threading.Thread(target=acct.draw, args=(900,), name='thread_2') thread_1.start() thread_2.start()
加锁时
#账户类 class Account: def __init__(self, account_no, balance): #账户编号和账户余额 self.account_no = account_no self.balance = balance self.lock = threading.RLock() def getBlance(self): return self.balance #提取现金方法 def draw(self, draw_amount): with self.lock: if self.balance >= draw_amount: print(threading.current_thread().name+'\t取钱成功!吐出钞票:'+str(draw_amount)) time.sleep(1) self.balance -= draw_amount print(threading.current_thread().name+'操作之后\t余额为:'+str(self.balance)) else: print(threading.current_thread().name+'\t取钱失败!余额不足!\t当前余额为:'+str(self.balance)) acct = Account('986623', 1500) thread_1 =threading.Thread(target=acct.draw, args=(800,), name='thread_1') thread_2 =threading.Thread(target=acct.draw, args=(900,), name='thread_2') thread_1.start() thread_2.start()
死锁
当两个线程互相等待对方释放各自要加锁的锁对象时(即两个线程占用着各自将要加锁的锁对象),就会产生死锁
出现死锁时,程序会出现僵持的状态并且无法继续执行下去
避免死锁问题
避免多次锁定: 尽量避免同一个线程对多个锁对象进行锁定
具有相同的加锁顺序: 多个线程对多个锁对象加锁时,应该保证它们以相同的顺序请求加锁
使用定时锁: 程序在调用acquire()方法加锁时可以指定timeout参数,超时会自动释放锁
死锁检测: 依靠算法机制来实现的死锁预防机制