Python线程实现银行账户存取款:如何避免余额为负?
银行账户线程安全存取款问题解决
原代码的问题在哪?
原代码的锁只护住了余额修改的步骤,但判断余额是否为负的操作在锁外面,这就导致了竞态问题:比如取款线程看到余额≥0,刚要执行取款,存款线程突然插进来把余额取成负数,取款线程接着执行就会让余额变负。而且没有办法让取款线程在余额不够时停下来等存款。
解决办法:用Condition条件变量
得用threading.Condition代替单纯的Lock,它不仅能实现互斥,还能让线程在条件不满足时进入等待,等其他线程触发信号再继续。
修改后的代码
import threading balance = 0 nb_transaction = 999999 counter = 0 # 用Condition,自带锁功能 money_cond = threading.Condition() def transaction(nb_transaction, n): global balance global counter for _ in range(nb_transaction): with money_cond: # 取款操作时,必须等余额足够才能继续 if n == -1: # 用while循环防止虚假唤醒,确保条件真的满足了才执行 while balance <= 0: money_cond.wait() # 执行存/取款操作 balance += n # 操作完通知所有等待的线程,让它们重新检查条件 money_cond.notify_all() t1 = threading.Thread(target=transaction, args=(nb_transaction, 1)) t2 = threading.Thread(target=transaction, args=(nb_transaction, -1)) t1.start() t2.start() t1.join() t2.join() print(counter) # 最终为0,不会出现余额为负的情况 print(balance) # 最终为0,存和取次数完全一致
关键逻辑说明
- Condition的使用:用
with语句自动管理锁的获取和释放,不用手动调用acquire()和release(),代码更简洁。 - 取款等待逻辑:取款线程发现余额不够时,调用
wait()释放锁并进入等待状态,直到存款线程完成操作后调用notify_all()唤醒它。用while循环而不是if,是因为线程可能被虚假唤醒(没等到通知就醒了),必须重新检查余额是否足够。 - 通知机制:每次存/取款完成后,唤醒所有等待的线程,让它们重新判断是否可以执行自己的操作。
- 最终余额保证为0:因为存和取的次数完全相同,而且取款只有在余额足够时才会执行,所以最后余额必然是0,
counter也会是0,不会出现余额为负的情况。
为啥原锁解决不了问题?
原代码里判断余额的操作在锁外面,多个线程可以同时读余额的值,判断和修改不是原子操作。比如线程2读到余额是1,刚要执行取款,线程1抢过来把余额改成0,线程2还是会执行取款,结果余额就变负了。只有把判断和修改都放到锁的保护范围内,再用Condition处理等待,才能彻底避免这个问题。
内容的提问来源于stack exchange,提问作者user398843
相关产品推荐
相关产品推荐

