生产者与消费者模型的应用:线程池

生产消费者模型 不过,为了维持生产消费者模型的正常运转,生产者和消费者之间也要存在同步关系,亦即相互之间根据彼此的状况对自身进行调整,否则,会出现供大于求的情况,导致冗余问题,或者供小于求的情况,导致饥饿问题。(1)解耦:对于一个普通的函数而言,其按部就班的从上到下运行,不过当涉及一个函数调用的时候,从调用这个函数、把参数传递给这个函数、再到这个函数把运行结果返回给主程序,在这段过程中,主程序是什么都不能做的,只能卡在那里,等待处理结果,这就会浪费很多时间。所以生产者之间是互斥的,而互斥的关系需要通过加锁来维持正常运转。 阅读详情

生产者消费者模型

生产者与消费者模型:大佬们针对典型的应用场景设计的解决方案
生产者与消费者模型应用场景:有线程不断地产生数据,有线程不断地处理数据。
数据的产生与数据的处理,如果放在同一个线程中完成,因为执行流只有一个,那么肯定是生产一个处理一个,处理完一个后才能生成一个。
这样的依赖关系太强了,如果处理的比较慢,就会把程序的速度拖慢下来。
因此将生产与处理放在不同的执行流中完成,那么生产和处理的线程如何实现通信呢?
中间增加一个数据缓冲区(队列),作为中间的数据缓冲场所。
产生线程只负责将数据放入缓冲区,处理数据只负责将数据从缓冲区中取出并处理。
在这里插入图片描述

生产者消费者模型的优点

  • 解耦合

    将模块分开,耦合度降低两边都会比较灵活,各自可以按照各自的业务压力创建线程处理,
    且处理模块代码发生改变只需要改变处理模块代码。

  • 支持忙闲不均

    缓冲区大小可以一直申请,是可调的,数据多了就可以放在缓冲区。

  • 支持并发

    可以有多个执行流进行处理

生产者消费者模型的优点很显然,生活中很多时候是请求多于响应的,根据这个模型,大佬们就设计了线程池。

线程池

一个或多个创建好的线程+线程安全pcb等待队列
其他线程将要处理的任务,添加到线程池任务队列中,线程池的线程不断从pcb等待队列中获取任务进行处理。
应用:应用于有大量请求任务需要处理的场景

	优点:
		避免了峰值压力下资源耗尽的风险
		避免了大量频繁创建与销毁线程带来的时间成本
		正常流程:任务处理T = 创建线程T1 + 任务处理过程 + 销毁线程T3
				这样的流程大量的时间都放在了创建和销毁线程,没有把所有的资源都放在任务处理上

实现:一个或多个创建好的线程 + 线程安全的任务队列
如何知道任务如何处理:

1.在线程入口函数中直接定义好任务处理逻辑 
    错误的理解
	这样写死的线程池只能处理特定的功能,达不到处理各式各样功能的目的
2.添加任务的线程,添加任务是,将要处理的数据和数据处理的方法一并传入线程池
	线程池中的线程只需要调用处理函数传入数据即可
	task{
		void* data;
		void(*handler)(void* data);
	}
	线程池中的线程只需要调用一下  handler(&data);就可以完成处理数据完成任务

设计一个简单的线程池

设计任务类:
当线程池的线程获取到任务对象时,只需要调用一下它的Run即可完成任务

//定义一个函数指针
typedef void(*handler_t)(int)
class ThreadTask
{
	private:
		int _data;    //要传入的数据
		handler_t _handler; //处理数据的方法
	public:
		void Run()
		{
			_handler(data);
		}
}

设计一个线程安全的任务 队列:
主要就是实现同步与互斥,这点可以使用信号量,可以使用条件变量都可以。
本文以条件变量实现为例:

#define MAXCAP 3    
class BlockQueue    
{    
 private:    
   std::queue<ThreadTask> q;    
   size_t cap;    
   pthread_mutex_t mutex;    
   pthread_cond_t Cus_cond;    
   pthread_cond_t Pro_cond;    
 public:    
   BlockQueue(size_t c = MAXCAP):cap(c)    
 {    
   //mutex=PTHREAD_MUTEX_INITIALIZER;      //C++不用这个    
   pthread_mutex_init(&mutex,NULL);    
   pthread_cond_init(&Cus_cond,NULL);    
   pthread_cond_init(&Pro_cond,NULL);    
 }    
   ~BlockQueue()                                                                                             
   {    
     pthread_mutex_destroy(&mutex);    
     pthread_cond_destroy(&Cus_cond);    
     pthread_cond_destroy(&Pro_cond);    
   }    
   bool push(const ThreadTask& data)
   {
     //要往缓冲区队列放数据,就得先判断缓冲区是否满了,所有线程都可以访问缓冲区,那么缓冲区就是临界资源
     //需要进行保护
     pthread_mutex_lock(&mutex);
     //循环条件防止时间片调度产生的访问出错
     while(q.size() == cap)
     {
       pthread_cond_wait(&Pro_cond,&mutex);//解锁阻塞加锁
     }
     q.push(data);
     pthread_cond_signal(&Cus_cond);
     pthread_mutex_unlock(&mutex);
     return true;
   }
   bool pop(ThreadTask* data)
   {
     pthread_mutex_lock(&mutex);                                                                             
     while(q.empty() == true)
     {
       pthread_cond_wait(&Cus_cond,&mutex);
     }
     *data= q.front();
     q.pop();
     pthread_cond_signal(&Pro_cond);
     pthread_mutex_unlock(&mutex);
     return true;
   }
};

可以自行实现一个,加上模板最好。
信号量版本
点击超链接,我在另一篇文章写到过,不再赘述。
实现一个线程池类
线程池类只需要创建几个线程不停地获取任务即可,所以

成员设计:
	1.最大线程数  -- 决定创建几个线程    _max_thread
	2.一个线程安全的任务队列,  BlockQueue    
成员方法:
	3.其他线程将任务入队,TaskPush(const ThreadTask& task);
	4.构造方法,创建指定数量的线程
	5.线程入口函数,出队获取任务,调用task.Run()
#define MAX_THREAD 5
#define MAX_QUEUE 10
class threadpool
{
  private:
    int _max_thread;                                                                                          
    BlockQueue _queue;
  private:
    //这个入口函数如果在类中声明,就会自动带有this指针,而入口函数的参数返回值是固定的,将他定义为static
    //就可以解决,但static后就变成了全局的,相当于每个线程调的都是这一个入口函数,也就达不到目的了     
    //将this通过参数传进来就可以解决问题                                                          
    static void* th_entry(void* arg)    
    {                               
      threadpool*pb = (threadpool*)arg;
      while(1)                         
      {       
        ThreadTask task;
        pb->_queue.pop(&task);   //获取到节点
        task.Run();                          
      }            
      return NULL;
    }
  public:
    threadpool(int maxt = MAX_THREAD,int maxq = MAX_QUEUE):_max_thread(maxt),_queue(maxq)
    {
      int ret;
      pthread_t tid[];
      for(int i = 0; i<_max_thread; ++i)
      {
        ret = pthread_create(&tid[i],NULL,th_entry,this);
        if(ret != 0)
        {
          exit(-1);  //构造函数没法判断成功与否,出问题就直接退出进程
        }
        pthread_detach(tid[i]);
      }
    
    }
    bool TaskPush(const ThreadTask& task)                                                                     
    {
      return _queue.push(task);
    }
};

线程池类设计注意点:
需要注意的是,线程的入口函数,在C语言中明确定义了格式,为void* entry(void*),必须是一个void类型的参数和void返回值,这是不能改变的,在类中创建线程,入口函数也要写在类中,但问题就来了。
类中的成员方法自带this指针,而这样函数参数多了一个,系统就检测不到这是入口函数,报错:线程入口函数未定义。想要解决,就需要在类外定义,这样未免太过麻烦。直接加上static好了,这样就不带this指针了。
但是问题又来了,我们知道,通过this指针才能确保线程调用自己对应的入口函数,如果没了this指针,而且,static修饰方法,这样一来所有的线程都掉用的是同一个entry了。我们如何确保线程调用的是自己的入口函数呢?
解决办法就是,将this指针作为参数在线程创建的时候传入入口函数,这样问题都解决了。
解决方法:static修饰入口函数 + this作参数

C++ 多线程通信方式简介并结合生产者-消费者模式代码实现 立即下载

相关推荐

ArcGIS供地图斑图形及属性入库操作(附练习数据)

已知数据: 供地图斑位置图。 供地图斑表格 成果要求:将供地图斑导入mdb数据库并将供地图斑表格的属性进行挂接。 接下来我们开始操作: 1、 打开arcmap,在目录内连接的文件夹内找到供地图斑位置图。图片 2、 将annotation和polygon添加到arcmap图片 3、 右击文件所在文件夹新建mdb数据库图片 4、 右击数据库新建要素数据集。(要素数据集非常重要,稍后所有操作都在数据集内进行)选择图斑投影方式及代号。图片 5、 右击要素数据集—导入(将图层内的数据导入数据库)图片 6、 数据导入.

qq_43173805的博客 8132

生产者-消费者模型详解

生产者-消费者模式是多线程编程中常见的一种模式,它用于解决生产者和消费者之间的协作问题。生产者负责生成数据,消费者负责处理数据,通过合理的协作,可以实现高效的数据处理。本文将详细介绍 Java 生产者-消费者模式,包括其基本概念、常见用法以及注意事项。

qq_65501447的博客 1636

ENU坐标系实战:5分钟搞定无人机导航中的站心坐标转换

本文针对无人机导航中GPS坐标与视觉感知不匹配的定位漂移问题,提供了基于ENU坐标系的实战解决方案。通过Python代码演示了如何快速、精准地实现地心地固坐标与站心坐标之间的转换,并详细解释了转换原理、精度对比及工程优化技巧,帮助开发者从根本上解决局部相对定位不准的难题。

mqtt6iot的博客 552

线程池(看这一篇足够了)

线程池,一篇小够

可乐醉了猫儿的博客 951

STM32F407单一定时器输出4路不同频率及占空比的PWM波

STM32F407单一定时器输出4路不同频率及占空比的PWM波

qq_43868701的博客 4919

实例讲解Simulink应用层开发CAN报文解包及CAN信号设置方法

本文通过讲解MCU CAN解包模块的模块设置、输入输出信号的设置及仿真测试验证,说明了CAN解包模块的设置方法、CAN解包模块输入输出信号的设置方法以及CAN解包模块的仿真测试及修正方法。在模型搭建过程中,建议养成一种搭建完成仿真测试验证的习惯,在模块阶段发现错误,解决错误,避免在后期模块集成之后报出大量错误,增加解决错误的工作量和难度。希望通过本文的介绍能给相关技术人员带来一些参考和帮助。

qq_43011586的博客 5681

基于线程池的生产者消费者模型(含阻塞队列)

基于阻塞队列、线程池的生产者消费者模型

challenglistic的博客 1033

ZYNQ程序固化实战:从BOOT.bin生成到QSPI/SD卡双启动模式详解

本文详细解析了ZYNQ 7000系列程序固化的完整流程,重点解决了程序断电丢失问题。通过实战演示,从生成BOOT.bin文件到配置QSPI与SD卡双启动模式,逐步指导开发者实现程序在外部存储中的永久驻留,确保产品脱离调试器独立运行。

weixin_29193259的博客 517

生产者消费者模式与实际应用

1. 前言 目前的项目需要在用户提交订单后,将订单放入队列,并由专门处理订单的线程对队列中的订单做处理,由此便引入了生产者与消费者模式。 2. 生产者与消费者模式概述 生产者与消费者模式是通过一个容器来解决生产者与消费者的强耦合关系,生产者与消费者之间不直接进行通讯,而是利用阻塞队列来进行通讯,生产者生成数据后直接丢给阻塞队列,消费者需要数据则从阻塞队列获取,实际应用中,生产者与消费者模式则

程序员进阶之路 7650

大数据NiFi(十一):NiFi入门案例一

注意:数据写出的目录可以不需要提前在对应节点上创建,默认是可以自动创建,可以通过“Create Missing Directories”属性配置。将输出文件的权限设置为此属性的值,格式必须是UNIX rwxrwxrwx,用-来代替被拒绝的权限(例如rw-r--r--)或八进制数字(例如644),可以使用表达式,例如${file.permissions}。这里需要使用到的处理器是“GetFile”和“PutFile”,完成以上需求对“GetFile”和“PutFile”相关属性进行配置。

Lansonli(蓝深李)的博客 1489

Xilinx FPGA平台GTX简易使用教程(五)GTX收发测试及示例工程介绍

GTX IP配置完了,你不得搞个回环测试一番?

数字IC/FPGA杂货铺,找一找,或许有你想要的~ 1万+

Linux C语言线程池实现:从生产者消费者模型到高性能服务器应用

在并发编程中,生产者消费者模型是解决任务调度与资源协调的经典范式,其核心原理是通过共享缓冲区实现解耦。线程池作为该模型的重要工程实践,通过复用线程资源、控制并发数量,有效避免了频繁创建销毁线程的系统开销。这一技术在高性能服务器开发中价值显著,能够提升吞吐量并防止资源耗尽。本文以Linux C语言环境为例,深入剖析线程池的实现细节,涵盖互斥锁、条件变量等同步原语的应用,并探讨其在网络服务器等场景中的实践。通过解析任务队列、线程安全及优雅退出等关键机制,帮助开发者构建稳定高效的并发处理框架。

weixin_30536861的博客 335

SAP_SD模块-销售批次策略应用记录

SAP_SD模块-销售批次策略完整案例

风沙的博客 2610

C++多线程优化实战:生产者-消费者模型与线程池在视频处理中的应用

多线程编程是现代软件开发中提升性能的核心技术,其原理在于利用多核CPU的并行计算能力,将任务分解为多个可同时执行的子任务。通过线程间的协同工作,可以显著提高程序的吞吐量和响应速度,在计算密集型和高并发场景中具有重要价值。生产者-消费者模型是实现线程间数据安全传递的经典设计模式,配合有界队列能有效控制内存使用,避免资源耗尽。线程池技术则通过复用线程来降低创建销毁开销,并控制并发度以匹配CPU核心数。这些技术广泛应用于视频流处理、图像分析、实时数据采集等场景。本文以视频抽帧与点云合成项目为例,详细探讨了如何通过

weixin_33910385的博客 636

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

操作系统 —— 生产者消费者模型

生产者消费者模型,生产者消费者无非是几个线程,关键是支持它们之间的场所如何实现?我给出三种实现方式:阻塞队列,环形队列,线程池。

lyzzs222的博客 3755

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

C++多生产者,多消费者模型

在C++标准库中实现多生产者多消费者模型,可以使用std::thread、std::queue、互斥锁(std::mutex)、条件变量(std::condition_variable)等组件。下面是一个简单的示例,展示如何创建多生产者和多消费者模型,并使用线程池的概念来限制线程的数量(虽然C++标准库没有直接提供线程池,但可以手动管理线程来模拟)。// 启动生产者线程。

slty_123的博客 1675

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

生产者消费者案例

Java 多线程 生产者 消费者 模型 Lock Condition synchronized

每个人为不同的理由带着面具说谎,动机也只有一种名字那 叫做欲望 289

线程池小结

简单用队列实现一下线程池

无聊星期三的博客 572

深入理解生产者-消费者模型:原理、实现与应用

生产者-消费者模型(Producer-Consumer Pattern)是一种常见的并发设计模式,用于解决多个线程之间共享数据的问题。生产者:负责生成数据或任务,并将其放入共享缓冲区消费者:从共享缓冲区获取数据或任务,并进行处理这种模式的核心思想是解耦生产者和消费者,使它们不必直接交互,而是通过一个共享的缓冲区进行通信。生产者-消费者模型是并发编程中最重要的设计模式之一,它通过缓冲区解耦生产者和消费者,提供了良好的可扩展性和灵活性。简单场景:推荐使用等现成工具复杂场景。

2301_79730192的博客 1148

多线程锁,线程池,消费者生产者模型

锁是怎么存在的? 在python建立之初,开发者目的是为了快速把语言开发出来,如果加上GIL(c语言加锁),切换时按照100条字节指令来进行线程间的切换 为什么加锁? 非线程安全,控制一段代码 1.lock = threading.BoundedSemaphore一次可以限制关卡,限制通行次数,不过这个是写死的 1 import time 2 import t...

weixin_33966365的博客 116

自定义线程池的重要参数------阻塞队列

BlockingQueue阻塞队列接口,是一个支持两个附加操作的队列。在队列为空时,获取元素的线程会等待队列变为非空。当队列元素已满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是往队列里拿出元素的线程。阻塞队列的角色就是充当生产者存放元素的容器,而消费者只从容器里拿出元素,这也在一定程度上平衡了二者之间的处理能力,解决了它们之间的强耦合关系,在保证并发安全的同时,提高了队列的存取效率。

m0_64529944的博客 455

Linux多线程(六):线程池

本文摘要: 线程池是一种基于池化技术的多线程管理方案,其核心思想是预先创建并维护一组可重用的线程资源,通过"空间换时间"和"复用"机制提升系统性能。文章首先通过共享单车的比喻形象说明池化技术的优势,然后重点分析了线程池的应用场景(如Web服务器处理短时任务)和实现原理。作者实现了三个版本的线程池(基础版、C++11版和模板版),其中基础版采用生产者-消费者模型,使用互斥锁和条件变量保证线程安全,通过封装原生线程库简化操作。测试结果表明线程池能有效避免频繁创建/销毁线程

Surplus886的博客 734

手撕线程池

因为编译器解析函数声明时是从左到右的,获取返回值的类型std::future<std::invoke_result_t<F, Args...>> 但F Args..的类型必须等参数传入后,才能确认。1.实现要解决 传入的函数F的返回值类型是什么。和单队列不同的地方是,Pop()工作线程获取任务时 如果没有任务,不会阻塞在Pop()而是会进行Swap()交换队列(生产者队列有数据或者非阻塞)循环处理队列中的任务,通过Pop()获取任务 输出型参数task带回任务,没有任务就回阻塞在Pop()函数中。

wws7920的博客 938

C++学习第三十一课:C++ 线程与多线程编程的应用

线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。

2401_84585615的博客 995

线程协作啊

生产者消费者模式 方法一: package com.kuang.gaoji; //测试:生产者消费者模型-->利用缓冲区解决:管程法 //生产者,消费者,产品,缓冲区 public class TestPC { public static void main(String[] args) { synContainer synContainer = new synContainer(); new Productor(synContainer).st.

m0_48219937的博客 105

C++线程池实战:从生产者消费者模型到工业级实现

并发编程是现代软件开发的核心技术之一,旨在通过同时执行多个任务来提升程序性能。其基本原理基于多线程与同步机制,通过合理调度计算资源来最大化利用多核CPU。在技术价值上,它能显著提高应用程序的吞吐量和响应速度,尤其适用于高并发服务器、数据处理等场景。其中,线程池作为一种关键的工程实践模式,通过预先创建并管理一组工作线程,避免了频繁创建销毁线程的开销,有效解决了资源管理和任务调度问题。本文聚焦于Linux环境下使用C++实现一个健壮的线程池,深入探讨了任务队列设计、线程生命周期管理以及优雅停机等核心机制,并针对

weixin_34199764的博客 329
上一篇: 线程间通信:信号量
下一篇: C++特性:多态、重写
Diligent_wu
博客等级 码龄6年 62粉丝 58原创
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值