Python 多线程编程-03-threading 模块 - Condition

目  录

1. 复杂线程同步

1.1 生产者消费者问题

1.2 threading.Conditon 基本原理

2. threading.Condition 

2.1 threading.Conditon 属性和方法

2.2 threading.Conditon 使用示范


Python 多线程编程目录

Python 多线程编程-01-threading 模块初识

Python 多线程编程-02-threading 模块-锁的使用

Python 多线程编程-03-threading 模块 - Condition

Python 多线程编程-04-threading 模块 - Event

Python 多线程编程-05-threading 模块 - Semaphore 和 BoundedSemaphore

Python 多线程编程-06-threading 模块 - Timer

Python 多线程编程-07-threading 模块 - Barrier

1. 复杂线程同步

       前面两章已经说过 threading 模块中互斥锁的使用,不管是 threading.Lock 还是 threading.RLock,这种互斥锁是最简单的线程同步机制,在实际工作中会有很多复杂情况是互斥锁无法解决的。而Python 提供的 Condition 对象提供了对复杂线程同步问题的支持。

1.1 生产者消费者问题

        常见的生产者消费者模型具体来讲,就是在一个系统中,存在生产者和消费者两种角色,它们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者消耗生成的资料,生产者和消费者之间共享的内存缓冲池大小可变,生产者数量可变,消费者数量可变。

        像这类问题,用简单的互斥锁就比较难以解决。

1.2 threading.Conditon 基本原理

        Condition 被称为条件变量,可以理解为一种进阶的锁,使用的基本原理如下:
        1、Condition对象的构造函数可以接受一个 Lock/RLock 对象作为参数,如果没有指定,则默认是可重入锁 RLock,生成的 Condition 对象会维护一个 Lock/RLock 和一个 waiting 池。

        2、线程调用 acquire 方法获得 Condition 对象,这一点和 Lock/RLock类似 。

        # wait() 方法、notify() 方法、notifiy_all() 方法都只能在线程调用 acquire 方法之后,线程调用 release 方法之前!

        3、线程调用 wait 方法时,线程会释放 Condition 内部的锁并进入 blocked 状态,同时在waiting 池中记录这个线程。

        4、线程调用 notify/notify_all 方法时,Condition 对象会从 waiting 池中挑选一个线程,通知其调用 acquire 方法尝试取到锁。线程只能通过notify方法唤醒,所以notifyAll的作用在于防止有的线程永远处于沉默状态。

        5、线程调用 release 方法释放 Condition 对象

2. threading.Condition 

2.1 threading.Conditon 属性和方法

        Condition 被称为条件变量,除了提供与 Lock、RLock 类似的 acquire() 和 release() 方法外,还提供了 wait() 、notify()、notifyAll() 方法。

threading.Conditon 属性和方法
序号属性和方法描述
1

acquire

(blocking=True)

锁住这个 Condidtion,然后返回一个布尔值。

`blocking` 指示这个 Condition 无法锁定时候,是否阻塞线程。

如果 `blocking` 是 False 且另外一个线程锁定了这个 Conditon,那么立刻返回 False。

如果 `blocking` 是 True 且另外一个线程锁定了这个 Conditon,那么则阻塞这个线程直到 Condition 被释放,获得之并返回 True。阻塞过程是可以被打断。
其他情况立刻返回 True。准确地说,如果当前的线程已经锁定了这个 Condition,那么内置的计数器加一,如果没有线程锁定这个 Condition,则获得这个锁,且内置的计数器初始化为 1。

这意味着 Conditon 是可重入的。

2release()释放这个 Condition,允许在阻塞等待队列中的其他线程获得该 Condition。这个 Condition 此刻必须在被锁定的状态,且是被同一个线程给锁定的,否则返回 RuntimeError。
请注意,如果这个 Condition 被同一个线程锁定了多次,那么也需要 release 多次才行。
3notify(n=1)

唤醒一个或者多个在等待这个 Condition 的线程。如果被唤醒的线程没有acquire 这个 Condition,那么报 RuntimeError。

这个方法最多唤醒 n 个 线程。如果没有线程等待,则无作为。

4notifyAll()唤醒所有在等待这个 Condition 的线程。如果被唤醒的线程没有锁定这个 Condition,那么报 RuntimeError。
5

wait

(timeout=None)

阻塞等待直到该 Condition 释放或者 timeout 到期。

如果被唤醒的线程没有acquire 这个 Condition,那么报 RuntimeError。
这个方法释放底层的锁,然后阻塞直到被其他线程 notify() 或者 notify_all() 唤醒,或者直到可选的 timeout 到期。一旦被唤醒或者 timeout 到期,它会再次 acquire 这个锁,并且返回。
timeout argument 如果出现且不为 None,它应该是指定操作超时的浮点数,以秒为单位

当基础锁是RLock时,不会使用其 release() 方法,因为这可能不会在打开锁时实际解锁多次递归获取。而是一个内部接口使用 RLock 类的多次被递归获取。另一个内部接口是然后用于在重新获取锁时恢复递归级别。

6wait_for(predicate, timeout=None)

等待直到条件变为 True。predicate 应该是一个可调用对象,且其结果为一个布尔值。timeout 用于给定最长的等待时长。

2.2 threading.Conditon 使用示范

下面的代码以生产者-消费者为例子,一个资源池最多可以存放 5 个物品,一次生产线程最多生成 5 个物品,消费者每次是取走一个物品,如果资源池中少于4个物品,则提醒生产线程继续生成物品。

请注意 Producer 中的等待代码 self.con.wait(),没有使用 timeout,这样它是会一直阻塞等待的。

而 Consumer 中的等待代码 self.con.wait(6),等到6秒后自动解除阻塞等待状态,请大家想想是为什么。

import threading
import time

max_goods_num=10
min_goods_num=4
con = threading.Condition()
num = 0

class Producer(threading.Thread):
    def __init__(self, con):
        self.con = con
        super().__init__()
 
    def run(self):
        global num
        print("*"*50)
        print("Coming in Producer ",time.ctime())
        self.con.acquire()
        for _ in range(max_goods_num):
            print("----------进入循环生成物品程序------------")
            print("开始生成物品")
            num += 1
            print("资源池里面物品的个数为:{}".format(num))
            time.sleep(1)
            if num == 5:
                print("资源池里面物品的个数已经到达五个, 无法继续生成了")
                self.con.notify()
                self.con.wait()
            
        self.con.release()
        print("Producer run exit")
 
class Consumer(threading.Thread):
    def __init__(self, con):
        self.con = con
        super().__init__()
 
    def run(self):
        print("*"*50)
        print("Coming in Consumer ",time.ctime())
        self.con.acquire()
        global num
        while num:
            print("----------进入循环消耗物品程序------------")
            num -= 1
            print("资源池里面物品剩余:{}".format(num))
            time.sleep(0.5)
            if num <min_goods_num :
                print("资源池里面物品数量小于 min_goods_num,需要添加!")
                self.con.notify()
                self.con.wait(6)
                
        self.con.release()
        print("Consumer run exit")
        

p = Producer(con)
c = Consumer(con)
p.start()
c.start()


'''

要是大家觉得写得还行,麻烦点个赞或者收藏吧,想个博客涨涨人气,非常感谢!

'''

  • 15
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

江南野栀子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值