Condition实现线程通信

Condition实现线程通信,第1张

概述当线程在系统中运行时,线程的调度具有一定的透明性,通常程序无法准确控制线程的轮换执行,如果有需要,Python 可通过线程通信来保证线程协调运行。 假设系统中有两个线程,这两个线程分别代表存款者和取钱者,现在假设系统有一种特殊的要求,即要求存款者和取钱者不断地重复存款、取钱的动作,而且要求每当存款者将钱存入指定账户后,取钱者就立即取出该笔钱。不允许存款者连续两次存钱,也不允许取钱者连续两次取钱。

当线程在系统中运行时,线程的调度具有一定的透明性,通常程序无法准确控制线程的轮换执行,如果有需要,Python 可通过线程通信来保证线程协调运行。

假设系统中有两个线程,这两个线程分别代表存款者和取钱者,现在假设系统有一种特殊的要求,即要求存款者和取钱者不断地重复存款、取钱的动作,而且要求每当存款者将钱存入指定账户后,取钱者就立即取出该笔钱。不允许存款者连续两次存钱,也不允许取钱者连续两次取钱。

为了实现这种功能,可以借助于 Condition 对象来保持协调。使用 Condition 可以让那些己经得到 Lock 对象却无法继续执行的线程释放 Lock 对象,Condition 对象也可以唤醒其他处于等待状态的线程。

将 Condition 对象与 Lock 对象组合使用,可以为每个对象提供多个等待集(wait-set)。因此,Condition 对象总是需要有对应的 Lock 对象。从 Condition 的构造器 __init__(self,lock=None) 可以看出,程序在创建 Condition 时可通过 lock 参数传入要绑定的 Lock 对象;如果不指定 lock 参数,在创建 Condition 时它会自动创建一个与之绑定的 Lock 对象。

Condition 类提供了如下几个方法:

acquire([timeout])/release():调用 Condition 关联的 Lock 的 acquire() 或 release() 方法。 wait([timeout]):导致当前线程进入 Condition 的等待池等待通知并释放锁,直到其他线程调用该 Condition 的 notify() 或 notify_all() 方法来唤醒该线程。在调用该 wait() 方法时可传入一个 timeout 参数,指定该线程最多等待多少秒。 notify():唤醒在该 Condition 等待池中的单个线程并通知它,收到通知的线程将自动调用 acquire() 方法尝试加锁。如果所有线程都在该 Condition 等待池中等待,则会选择唤醒其中一个线程,选择是任意性的。 notify_all():唤醒在该 Condition 等待池中等待的所有线程并通知它们。


本例程序中,可以通过一个旗标来标识账户中是否已有存款,当旗标为 False 时,表明账户中没有存款,存款者线程可以向下执行,当存款者把钱存入账户中后,将旗标设为 True,并调用 Condition 的 notify() 或 notify_all() 方法来唤醒其他线程。

当存款者线程进入线程体后,如果旗标为 True,就调用 Condition 的 wait() 方法让该线程等待。当旗标为 True 时,表明账户中已经存入了钱,取钱者线程可以向下执行,当取钱者把钱从账户中取出后,将旗标设为 False,并调用 Condition 的 notify() 或 notify_all() 方法来唤醒其他线程;当取钱者线程进入线程体后,如果旗标为 False,就调用 wait() 方法让该线程等待。

本程序为 Account 类提供了 draw() 和 deposit() 两个方法,分别对应于该账户的取钱和存款 *** 作。因为这两个方法可能需要并发修改 Account 类的 self.balance 成员变量的值,所以它们都使用 Lock 来控制线程安全。除此之外,这两个方法还使用了 Condition 的 wait() 和 notify_all() 来控制线程通信。

import threadingclass Account:    # 定义构造器    def __init__(self,account_no,balance):        # 封装账户编号、账户余额的两个成员变量        self.account_no = account_no        self._balance = balance        self.cond = threading.Condition()        # 定义代表是否已经存钱的旗标        self._flag = False    # 因为账户余额不允许随便修改,所以只为self._balance提供getter方法    def getBalance(self):        return self._balance    # 提供一个线程安全的draw()方法来完成取钱 *** 作    def draw(self,draw_amount):        # 加锁,相当于调用Condition绑定的Lock的acquire()        self.cond.acquire()        try:            # 如果self._flag为假,表明账户中还没有人存钱进去,取钱方法阻塞            if not self._flag:                self.cond.wait()            else:                # 执行取钱 *** 作                print(threading.current_thread().name                    + " 取钱:" +  str(draw_amount))                self._balance -= draw_amount                print("账户余额为:" + str(self._balance))                # 将标识账户是否已有存款的旗标设为False                self._flag = False                # 唤醒其他线程                self.cond.notify_all()        # 使用finally块来释放锁        finally:            self.cond.release()    def deposit(self,deposit_amount):        # 加锁,相当于调用Condition绑定的Lock的acquire()        self.cond.acquire()        try:            # 如果self._flag为真,表明账户中已有人存钱进去,存钱方法阻塞            if self._flag:            #                self.cond.wait()            else:                # 执行存款 *** 作                print(threading.current_thread().name                    + " 存款:" +  str(deposit_amount))                self._balance += deposit_amount                print("账户余额为:" + str(self._balance))                # 将表示账户是否已有存款的旗标设为True                self._flag = True                # 唤醒其他线程                self.cond.notify_all()        # 使用finally块来释放锁        finally:            self.cond.release()#  定义一个函数,模拟重复max次执行取钱 *** 作def draw_many(account,draw_amount,max):    for i in range(max):        account.draw(draw_amount)#  定义一个函数,模拟重复max次执行存款 *** 作def deposit_many(account,deposit_amount,max):    for i in range(max):        account.deposit(deposit_amount)# 创建一个账户acct = Account("1234567",0)# 创建、并启动一个“取钱”线程threading.Thread(name="取钱者11",target=draw_many,args=(acct,800,50)).start()threading.Thread(name="取钱者12",50)).start()# 创建、并启动一个“存款”线程threading.Thread(name="存款者1",target=deposit_many,50)).start();threading.Thread(name="存款者2",50)).start()threading.Thread(name="存款者3",50)).start()

上面程序使用 Condition 的 wait() 和 notify_all() 方法进行控制,对存款者线程而言,当程序进入 deposit() 方法后,如果 self._flag 为 True,则表明账户中已有存款,程序调用 Condition 的 wait() 方法被阻塞;否则,程序向下执行存款 *** 作,当存款 *** 作执行完成后,系统将 self._flag 设为 True,然后调用 notify_all() 来唤醒其他被阻塞的线程。如果系统中有存款者线程,存款者线程也会被唤醒,但该存款者线程执行到 ① 号代码处时再次进入阻塞状态,只有执行 draw() 方法的取钱者线程才可以向下执行。同理,取钱者线程的运行流程也是如此。程序中的存款者线程循环 100 次重复存款,而取钱者线程则循环 100 次重复取钱,存款者线程和取钱者线程分别调用 Account 对象的 deposit()、draw() 方法来实现。主程序可以启动任意多个“存款”线程和“取钱”线程,可以看到所有的“取钱”线程必须等“存款”线程存钱后才可以向下执行,而“存款”线程也必须等“取钱”线程取钱后才可以向下执行。

总结

以上是内存溢出为你收集整理的Condition实现线程通信全部内容,希望文章能够帮你解决Condition实现线程通信所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/langs/1191628.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-06-03
下一篇2022-06-03

发表评论

登录后才能评论

评论列表(0条)

    保存