Python互斥锁(Lock):解决多线程安全问题

  • 内容
  • 评论
  • 相关

多线程的优势在于并发性,即可以同时运行多个任务。但是当线程需要使用共享数据时,也可能会由于数据不同步产生“错误情况”,这是由系统的线程调度具有一定的随机性造成的。

互斥锁的作用就是解决数据不同步问题。关于互斥锁,有一个经典的“银行取钱”问题。银行取钱的基本流程可以分为如下几个步骤:

  1. 用户输入账户、密码,系统判断用户的账户、密码是否匹配。
  2. 用户输入取款金额。
  3. 系统判断账户余额是否大于取款金额。
  4. 如果余额大于取款金额,则取款成功;如果余额小于取款金额,则取款失败。

乍一看上去,这确实就是日常生活中的取款流程,这个流程没有任何问题。但一旦将这个流程放在多线程并发的场景下,就有可能出现问题。注意,此处说的是有可能,并不是一定。也许你的程序运行了一百万次都没有出现问题,但没有出现问题并不等于没有问题!

按照上面的流程编写取款程序,并使用两个线程分别模拟两个人使用同一个账户做并发取钱操作。此处忽略检查账户和密码的操作,仅仅模拟后面三步操作。下面先定义一个账户类,该账户类封装了账户编号和余额两个成员变量。

class Account:
    # 定义构造器
    def __init__(self, account_no, balance):
        # 封装账户编号、账户余额的两个成员变量
        self.account_no = account_no
        self.balance = balance

接下来程序会定义一个模拟取钱的函数,该函数根据执行账户、取钱数量进行取钱操作,取钱的逻辑是当账户余额不足时无法提取现金,当余额足够时系统吐出钞票,余额减少。

程序的主程序非常简单,仅仅是创建一个账户,并启动两个线程从该账户中取钱。程序如下:

import threading
import time
import Account

# 定义一个函数来模拟取钱操作
def draw(account, draw_amount):
    # 账户余额大于取钱数目
    if account.balance >= draw_amount:
        # 吐出钞票
        print(threading.current_thread().name\
            + "取钱成功!吐出钞票:" + str(draw_amount))
#        time.sleep(0.001)
        # 修改余额
        account.balance -= draw_amount
        print("\t余额为: " + str(account.balance))
    else:
        print(threading.current_thread().name\
            + "取钱失败!余额不足!")
# 创建一个账户
acct = Account.Account("1234567" , 1000)
# 模拟两个线程对同一个账户取钱
threading.Thread(name='甲', target=draw , args=(acct , 800)).start()
threading.Thread(name='乙', target=draw , args=(acct , 800)).start()

先不要管程序中第 12 行被注释掉的代码,上面程序是一个非常简单的取钱逻辑,这个取钱逻辑与实际的取钱操作也很相似。

多次运行上面程序,很有可能都会看到如图 1 所示的错误结果。


线程安全问题
图 1 线程安全问题

本文标题:Python互斥锁(Lock):解决多线程安全问题

本文地址:http://www.hosteonscn.com/6409.html

评论

0条评论

发表评论

邮箱地址不会被公开。 必填项已用*标注