当前位置: 首页 > news >正文

Python 生产者消费者模型是什么?

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注!

作者| 慕课网精英讲师 朱广蔚

1. 简介

生产者和消费者问题是线程模型中的经典问题:

  • 生产者和消费者共享同一个存储空间
  • 生产者往存储空间中添加产品,消费者从存储空间中取走产品
  • 当存储空间为空时,消费者阻塞,当存储空间满时,生产者阻塞

Python 的内置模块 queue 提供了对生产者和消费者模型的支持,模块 queue 定义了类 Queue,类 Queue 表示一个被生产者和消费者共享的队列,类 Queue 提供如下常用方法:

方法

功能

get()

从队列中取走数据,如果队列为空,则阻塞

put(item)

向队列中放置数据,如果队列为慢,则阻塞

join()

如果队列不为空,则等待队列变为空

task_done()

消费者从队列中取走一项数据,当队列变为空时,唤醒调用 join() 的线程

2. 实现生产者消费者模型

创建生产者线程和消费者线程,使用一个共享队列连接这两个线程,代码如下:

import threading
import queueq = queue.Queue()
代码块1234
  • 导入 threading 模块和 queue 模块
  • 创建共享队列 q
def produce():for item in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']:q.put(item)print('produce %s' % item)
代码块1234
  • 创建生产者线程的入口函数 produce
  • 生产者生产 8 个数据
  • 调用 q.put(item) 将生产的数据放入到共享队列 q 中
def consume():for i in range(8):item = q.get()print('  consume %s' % item)
代码块1234
  • 创建消费者线程的入口函数 consume
  • 消费者消费 8 个数据
  • 调用 q.get() 从共享队列 q 中取走数据
producer = threading.Thread(target=produce, args=())
consumer = threading.Thread(target=consume, args=())
producer.start()
consumer.start()
producer.join()
consumer.join()
代码块123456
  • 创建生产者线程 producer,线程入口为 produce
  • 创建消费者线程 consumer,线程入口为 consume
  • 启动生产者线程和消费者线程,并等待它们结束

运行程序,输出结果如下:

produce a
produce bconsume a
produce cconsume bconsume c
produce dconsume d
produce econsume e
produce fconsume f
produce gconsume g
produce hconsume h
代码块12345678910111213141516
  • 生产者生产了 8 个数据:a、b、c、d、e、f、g、h
  • 消费者取走了 8 个数据:a、b、c、d、e、f、g、h

3. 实现生产者、计算者、消费者模型

创建生产者、计算者、消费者线程:

  • 生产者生产 8 个数据
  • 计算者对生产者输出的数据进行加工,将加工后的数据送往消费者
  • 消费者取走计算者输出的数据
import threading
import queueq0 = queue.Queue()
q1 = queue.Queue()
代码块12345
  • 导入模块 threading 和模块 queue
  • 使用两个共享队列连接这三个线程共享队列 q0 连接生产者和计算者共享队列 q1 连接计算者和消费者
def produce():for item in ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h']:q0.put(item)print('produce %s' % item)
代码块1234
  • 创建生产者线程的入口函数 produce
  • 生产者生产 8 个数据
  • 调用 q0.put(item) 将生产的数据放入到共享队列 q0 中
def compute():for i in range(8):item = q0.get()item = item.upper() q1.put(item)
代码块12345
  • 创建计算者线程的入口函数 compute
  • 调用 q0.get() 读取生产者输出数据,并进行加工
  • 调用 q1.put(item) 将加工后的数据放入到共享队列 q1 中
def consume():for i in range(8):item = q1.get()print('  consume %s' % item)
代码块1234
  • 创建消费者线程的入口函数 consume
  • 消费者消费 8 个数据
  • 调用 q1.get() 从共享队列 q1 中取走数据
producer = threading.Thread(target=produce, args=())
computer = threading.Thread(target=compute, args=())
consumer = threading.Thread(target=consume, args=())
producer.start()
computer.start()
consumer.start()producer.join()
computer.join()
consumer.join()
代码块12345678910
  • 创建生产者线程 producer,线程入口为 produce
  • 创建计算者线程 computer,线程入口为 compute
  • 创建消费者线程 consumer,线程入口为 consume
  • 启动生产者线程、计算者线程、消费者线程,并等待它们结束

运行程序,输出结果如下:

produce a
produce b
produce cconsume A
produce d
produce econsume B
produce fconsume C
produce gconsume D
produce hconsume Econsume Fconsume Gconsume H
代码块12345678910111213141516
  • 生产者生产了 8 个数据:a、b、c、d、e、f、g、h
  • 计算者将数据加工为:A、B、C、D、E、F、G、H
  • 消费者取走了 8 个数据:A、B、C、D、E、F、G、H

4. 同步生产者与消费者的推进速度

在生产者、消费者模型中,可能会存在两者推进速度不匹配的问题:生产者生产数据的速度较快,但是,消费者取走数据的速度较慢。

可以使用 queue 的 task_done() 方法和 join() 方法同步生产者与消费者的推进速度:

  • 生产者调用 join() 方法,等待队列中所有的数据被取走
  • 消费者调用 task_done() 方法,表示取走了队列中的一项数据,当队列为空时,唤醒阻塞在 join() 方法中的生产者
import threading
import queueq = queue.Queue()
代码块1234
  • 导入 threading 模块和 queue 模块
  • 创建共享队列 q
def produce():for item in ['A', 'B', 'C', 'D']:q.put(item)print('produce %s' % item)q.join()print('------------ q is empty')for item in ['E', 'F', 'G', 'H']:q.put(item)            print('produce %s' % item)q.join()        print('------------ q is empty')
代码块123456789101112
  • 创建生产者线程的入口函数 produce
  • 首先,生产 4 个数据:A、B、C、D调用 q.put(item) 将它们放入到队列 q 中调用 q.join() 等待消费者将它们全部取走
  • 然后,生产 4 个数据:E、F、G、G调用 q.put(item) 将它们放入到队列 q 中调用 q.join() 等待消费者将它们全部取走
def consume():for i in range(8):item = q.get()print('  consume %s' % item)q.task_done()
代码块12345
  • 创建消费者线程的入口函数 consume
  • 调用 q.get() 从队列 q 中取走一个数据
  • 调用 q.task_done(),表示已经从队列 q 中取走了一个数据,当队列为空时,唤醒生产者
producer = threading.Thread(target=produce, args=())
consumer = threading.Thread(target=consume, args=())
producer.start()
consumer.start()
代码块1234
  • 创建生产者线程 producer,线程入口为 produce
  • 创建消费者线程 consumer,线程入口为 consume
  • 启动生产者线程和消费者线程,并等待它们结束

运行程序,输出结果如下:

produce A
produce Bconsume Aconsume B
produce Cconsume C
produce Dconsume D
------------ q is empty
produce Econsume E
produce Fconsume F
produce G
produce Hconsume Gconsume H
------------ q is empty
代码块123456789101112131415161718
  • 生产者生产第一批数据 A、B、C、D,消费者将其取走
  • 当第一批数据完全被消费者取走后,生产者才开始生产第二批数据
  • 生产者生产第二批数据 E、F、G、H,消费者将其取走

欢迎关注「慕课网」,发现更多IT圈优质内容,分享干货知识,帮助你成为更好的程序员!

相关文章:

Python 生产者消费者模型是什么?

本文首发自「慕课网」,想了解更多IT干货内容,程序员圈内热闻,欢迎关注! 作者| 慕课网精英讲师 朱广蔚 1. 简介 生产者和消费者问题是线程模型中的经典问题: 生产者和消费者共享同一个存储空间生产者往存储空间中添…...

手机银行评测系列:北京银行“京彩生活”7.0从用户视角出发,实现沉浸式体验重塑

易观:2023年3月28日,北京银行发布“京彩生活”APP 7.0版本,从旅程再造、特色金融、场景生态、平台联动、协同经营、体验管理和安全守护七大方面全面升级,从用户视角出发,重塑用户旅程,简化操作流程&#xf…...

ZJYC2023 浙江省大学生程序设计竞赛校内选拔赛部分题解 C J B L

ZJYC2023 浙江省大学生程序设计竞赛校内选拔赛部分题解 C J B L 难度分布&#xff1a; 签到&#xff1a;CJ Easy&#xff1a;BL Midium&#xff1a;IAGKFE Hard&#xff1a;DH 题解&#xff1a; 签到&#xff1a;CJ C - ^{-1} 参考代码&#xff1a; #include<bits/std…...

百科创建:7种有效的百科词条创建技巧

百科词条是互联网上最常见的知识信息资源之一&#xff0c;它们是人们查找信息的主要途径之一。创建一个高质量的百科词条并不是一件容易的事情&#xff0c;需要一些技巧和经验才能做到。下面是一些创建百科词条的技巧&#xff1a; 一、确保词条的独特性 在创建百科词条之前&…...

ThreeJS-dat.gui界面控制颜色、隐藏、位置(六)

下载组件dat.gui npm install dat.gui -S 引入组件 import * as dat from dat.gui //界面控制 代码&#xff1a; <template> <div id"three_div"> </div> </template> <script> import * as THREE from "three"; import {O…...

接口自动化测试,完整入门篇

目录 1. 什么是接口测试2. 基本流程3. 需求分析4. 用例设计5. 脚本开发6. 结果分析7. 完整脚本8. 参考资料1. 什么是接口测试 顾名思义&#xff0c;接口测试是对系统或组件之间的接口进行测试&#xff0c;主要是校验数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及…...

利用ControlNet重新定义你的AI姿势

利用ControlNet重新定义你的AI姿势 前段时间给大家分享了如何利用colab实现AI绘画自由&#xff0c;现在Stable Diffusion WebUI Colab TW又更新了不少新功能。最重要的是可以通过谷歌硬盘的快捷方式导入模型&#xff0c;极大的节省了谷歌硬盘容量。 众所周知&#xff0c;谷歌…...

中医药NER命名实体识别基于SPANNER方式

一个不知名大学生&#xff0c;江湖人称菜狗 original author: Jacky Li Email : 3435673055qq.com Time of completion&#xff1a;2023.3.5 Last edited: 2023.3.5 导读 本文使用SPANNER方式实现对中医药进行实体识别&#xff0c;采用focal loss 进行优化。 本文章作用防止安静…...

Vue必掌握

目录 一、组件通信方式 二、v-if和v-for 三、生命周期 1、描述 2、setup和created谁先执行 3、setup中为什么没有beforeCreate和created 四、双向绑定 v-model 1、定义 2、本质&#xff0c;原理 3、好处 五、如何扩展一个组件 1、mixins 缺点 2、slot插槽 3、e…...

SSM部分

声明式事务 从之前的事务控制的代码中可以看出&#xff0c;是有规律可循&#xff0c;代码的结构基本是确定的&#xff0c;所以框架就可以将固定模式的代码抽取出来&#xff0c;进行相关的封装。 封装起来后&#xff0c;我们只需要在配置文件中进行简单的配置即可完成操作。 …...

【Springboot系列】Springboot接管所有Controller,magic-api源码阅读

系列文章地址:Spring Boot学习大纲,可以留言自己想了解的技术点 最近在项目中使用了一个第三方的包 magic-api,节省了很多的时间,整体来说就是只用写sql就好了,不用写service,controller那些,全部统一处理了。 具体的使用大家可以搜索下,网上到处都是,建议去官网看。…...

二、LED子系统数据结构详解

个人主页&#xff1a;董哥聊技术我是董哥&#xff0c;嵌入式领域新星创作者创作理念&#xff1a;专注分享高质量嵌入式文章&#xff0c;让大家读有所得&#xff01;文章目录1、核心数据结构1.1 gpio_led_platform_data1.2 gpio_leds_priv1.3 gpio_led1.4 gpio_led_data1.5 led_…...

Kubernetes(11):数据存储详解

在前面已经提到,容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的数据,kubernetes引入了Volume的概念。 Volume是Pod中能够被多个容器访问的共享目录…...

随想录Day43--动态规划: 1049. 最后一块石头的重量 II , 494. 目标和 , 474.一和零

最后一块石头重量转化为将一个集合分隔成两个集合&#xff0c;两个集合之间的差值最小&#xff0c;就是最后剩下最小的石头重量。这里可以求集合的一个平均值&#xff0c;如果正好等于平均值&#xff0c;说明可以抵消&#xff0c;这时候重量为0&#xff0c;如果不行&#xff0c…...

Qt中对TCP粘包的处理

当时用TCP协议传输数据时&#xff0c;经常出现粘包的现象 当服务器向客户端发送数据之后&#xff0c;客户端还没有接收数据的时候&#xff0c;这段时间数据在什么地方&#xff1f; 1、服务器&#xff1f;服务器已经发出数据了 2、网线&#xff1f;数据应该在内存&#xff0c;怎…...

贪心-单调递增的数字

当且仅当每个相邻位数上的数字 x 和 y 满足 x < y 时&#xff0c;我们称这个整数是单调递增的。 给定一个整数 n &#xff0c;返回 小于或等于 n 的最大数字&#xff0c;且数字呈 单调递增 。 示例 1: 输入: n 10 输出: 9示例 2: 输入: n 1234 输出: 1234示例 3: 输入…...

你真的会用搜索引擎吗?

作为一名在校大学生&#xff0c;对于搜索资料这一件事深有体会&#xff0c;特别是在期末考试突击的时候&#xff0c;如何利用搜索引擎&#xff0c;快速找到自己想要的知识&#xff0c;快速理解这个知识点&#xff0c;想必是每位大学生的必备技能了。 我们在学习一个知识点的过…...

KDCJ-20kV冲击耐压测试仪

一、产品简介 KDCJ-20kV冲击耐压测试仪是电力设备高压试验的基本项目之一&#xff0c;电力设备在设计、制造及修缮之后都要求进行冲击试验以验证或检验。因此&#xff0c;冲击电压试验设备有着广泛的应用&#xff0c;在工厂、研究机构及大专院校的高压试验室中都可以看到不同规…...

【Mybatis源码分析】TypeAliasRegistry源码分析

TypeAliasRegistry源码分析一、引入类型别名二、typeAlias 的三种配置方式三、TypeAliasRegistry源码分析三种配置方式源码解析校验过程Mybatis默认的别名配置四、总结一、引入类型别名 当配置 XML 文件&#xff0c;需要指明Java类型时&#xff0c;类型别名可替代Java类型的全…...

节点高负载

如何判断节点高负载? 可以通过 top 或 uptime 来确定 load 大小,如果 load 小于 CPU 数量,属于低负载,如果大于 CPU 数量 2~3 倍,就比较高了,当然也看业务敏感程度,不太敏感的大于 4 倍算高负载。 排查思路 观察监控:通常不是因为内核 bug 导致的高负载,在卡死之前…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

376. Wiggle Subsequence

376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...

深度学习习题2

1.如果增加神经网络的宽度&#xff0c;精确度会增加到一个特定阈值后&#xff0c;便开始降低。造成这一现象的可能原因是什么&#xff1f; A、即使增加卷积核的数量&#xff0c;只有少部分的核会被用作预测 B、当卷积核数量增加时&#xff0c;神经网络的预测能力会降低 C、当卷…...

基于SpringBoot在线拍卖系统的设计和实现

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 在线拍卖系统&#xff0c;主要的模块包括管理员&#xff1b;首页、个人中心、用户管理、商品类型管理、拍卖商品管理、历史竞拍管理、竞拍订单…...

Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换

目录 关键点 技术实现1 技术实现2 摘要&#xff1a; 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式&#xff08;自动驾驶、人工驾驶、远程驾驶、主动安全&#xff09;&#xff0c;并通过实时消息推送更新车…...

C++ 设计模式 《小明的奶茶加料风波》

&#x1f468;‍&#x1f393; 模式名称&#xff1a;装饰器模式&#xff08;Decorator Pattern&#xff09; &#x1f466; 小明最近上线了校园奶茶配送功能&#xff0c;业务火爆&#xff0c;大家都在加料&#xff1a; 有的同学要加波霸 &#x1f7e4;&#xff0c;有的要加椰果…...

深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏

一、引言 在深度学习中&#xff0c;我们训练出的神经网络往往非常庞大&#xff08;比如像 ResNet、YOLOv8、Vision Transformer&#xff09;&#xff0c;虽然精度很高&#xff0c;但“太重”了&#xff0c;运行起来很慢&#xff0c;占用内存大&#xff0c;不适合部署到手机、摄…...

springboot 日志类切面,接口成功记录日志,失败不记录

springboot 日志类切面&#xff0c;接口成功记录日志&#xff0c;失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...