Python 多线程编程-03-threading 模块 - Condition_threading.condition-程序员宅基地

技术标签: python  # Python 多线程编程  多线程  threading  后端  开发语言  

目  录

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 是可重入的。

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

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

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

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

wait

(timeout=None)

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

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

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

6 wait_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()


'''

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

'''

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u010701274/article/details/122880222

智能推荐

数据库数据是Date时间类型的 在后台查询时显示的都正常 但是传到前台时发现少了一天_pgsql的date类型后台返回时间正常前端使用时间有误-程序员宅基地

文章浏览阅读2.7k次,点赞2次,收藏4次。这是因为时区问题 咱们这是东八区(&serverTimezone=GMT%2B8,数据库的配置,意思即为东八区北京时间)今天这是在使用springboot向小程序前端返回时间格式的数据时发现的问题。这是因为在springboot中,无论时Controller还是RestController他们的返回类型都是Json类型的,而springboot默认的是Jackson框架转换,而J..._pgsql的date类型后台返回时间正常前端使用时间有误

计算机视觉(一)-openCV的安装及使用_kali opencv-程序员宅基地

文章浏览阅读193次。计算机视觉(一)-openCV的安装及使用下面链接是我博客园网址,对opencv安装以及配置VS2019环境进行了详细的说明,欢迎大家阅读:地址_kali opencv

python MQTT多线程接收与数据上传MySQL_python mqtt 多线程-程序员宅基地

文章浏览阅读7.4k次,点赞2次,收藏23次。承接上篇博客null试了很久,发现是sleep()函数问题。我不太清楚是什么问题,就是程序运行一段时间就停止了,也不会报错,有大佬知道是为什么的可以说一下么,谢谢了。下面是我实现的MQTT订阅消息代码。import paho.mqtt.client as mqttfrom datetime import datetimeimport jsonimport timeimport pymysql as MySQLdbMQTTHOST = "192.168.31.67"MQTTPORT = 1883mqttClh_python mqtt 多线程

spring 整合editor.md 实现markdown 编辑_spring引入markdown-程序员宅基地

文章浏览阅读3.4k次,点赞2次,收藏11次。整合一个编辑器,其实很简单。首先找到他的源码,以editor.md 为例:https://github.com/pandao/editor.md 下载后,到examples文件夹看看,看到一个simple.html的例子,这就是可以复制的前端代码:到myeclipse中新建一个应用, 并添加spring支持(公司用jdk1.7,就简单用spring4.2.5, 不用spring boot..._spring引入markdown

chapter two -2-1顺序表插入 & 2-2 顺序表初始化_include<sequencelist.h>-程序员宅基地

文章浏览阅读165次。2-1 线性表抽象数据类型线性表定义:0个 or 多个数据元素的有限序列特点:是一个序列 元素之间是有序的 数据元素 之间是 一对一的关系有限性: 线性表的数据元素的个数是有限的 (食物链中生物的个数是有限的)常见的操作创建和初始化 、 查找 、 插入 、 删除(增删改查)线性表表达:ADT 线性表(SequenceList) //线性表Data 1..._include

12.6习题_var name = "小白"; var obj = { name: "小红" }; functio-程序员宅基地

文章浏览阅读190次。1.发布订阅者我们已经知道实现数据的双向绑定,首先要对数据进行劫持监听,需要设置一个监听器Observer,用来监听所有属性。如果属性发上变化了,就需要告诉订阅者Watcher看是否需要更新。因为订阅者是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者,然后在监听器(发布者)Observer和订阅者Watcher之间进行统一管理的。接着,我们还需要有一个指令解析器Compile,..._var name = "小白"; var obj = { name: "小红" }; function sayname() { return t

随便推点

Java 5种方法实现生产者消费者问题与2种方法实现读者写者问题_读写者问题和消费者生产者问题哪个好实现-程序员宅基地

文章浏览阅读292次。摘要: Java实现生产者消费者问题与读者写者问题1、生产者消费者问题 生产者消费者问题是研究多线程程序时绕不开的经典问题之一,它描述是有一块缓冲区作为仓库,生产者可以将产品放入仓库,消费者则可以从仓库中取走产品。解决生产者/消费者问题的方法可分为两类:(1)采用某种机制保护生产者和消费者之间的同步;(2)在生产者和消费者之间建立一个管道。第一种方式有较高的效_读写者问题和消费者生产者问题哪个好实现

UGUI源码解读-布局系统_graphicupdatecomplete-程序员宅基地

文章浏览阅读387次。CanvasUpdate枚举类:CanvasUpdate,用于表示当前的布局阶段public enum CanvasUpdate{ /// <summary> /// Called before layout. /// </summary>Prelayout= 0, /// <summary> /// Called for layout. /// </summary>Layout= 1, ///_graphicupdatecomplete

Ambari安装之安装并配置Ambari-server(三)-程序员宅基地

文章浏览阅读324次。   不多说,直接上干货!   前期博客 Ambari安装之部署本地库(镜像服务器)(二) 安装并配置Ambari-server(1)检查仓库是否可用 [hadoop@ambari01 yum.repos.d]$ pwd/etc/yum.repos.d[hadoop@ambari01 yum.repos.d]$ y..._怎么确定ambari server

报错kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 26s-程序员宅基地

文章浏览阅读967次。报错kernel:NMI watchdog: BUG: soft lockup - CPU#0 stuck for 26s故障背景近期在服务器跑大量高负载程序,造成cpu soft lockup。如果确认不是软件的问题。解决方法#追加到配置文件中echo 30 > /proc/sys/kernel/watchdog_thresh #查看[root@git-node1 data]# tail -1 /proc/sys/kernel/watchdog_thresh30#临时生效_kernel:nmi watchdog: bug: soft lockup - cpu#0 stuck for

ECharts x坐标轴(xAxis)系列设置_echarts x轴坐标样式-程序员宅基地

文章浏览阅读3.7k次。aAxis:{ show:true,//false data:['1月','2月','3月'], boundaryGap:false,// true | ['30%', '20%'],x轴两边是否留白,true留白,false不留白}_echarts x轴坐标样式

MarkDown仿Qt Creator帮助文档样式表_markdown转qt help-程序员宅基地

文章浏览阅读249次。一直觉得Qt的帮助文档的样式完美的长在了我的审美点上,因此在使用MarkDown编写自己的文档时也喜欢做成类似于Qt帮助文档的样式_markdown转qt help

推荐文章

热门文章

相关标签