生产者消费者模式最佳实践

article/2025/9/29 9:20:21

测试环境:ubuntu18.04+opencv4.2+Qt

        一个生产者-消费者模式下的视频处理框架。基础结构:视频读取类线程不断读取视频帧,处理类线程对图像进行处理,之后通过信号与槽机制在主线程中显示。特点:视频读取、处理为独立线程,主线程中只做显示调度。

应用一、显示主线程等待处理线程处理完后显示图像。

首先需要定义全局变量:

g_base.h文件:

#ifndef G_BASE_H
#define G_BASE_H#include <QMutex>
#include <QWaitCondition>
#include <opencv2/opencv.hpp>extern cv::Mat g_img; // 全局图像
extern int g_fNum; // 全局图像帧计数器extern QMutex g_m_reader;
extern QWaitCondition g_wc_reader;extern QMutex g_m_proc;
extern QWaitCondition g_wc_proc;#endif // G_BASE_H

g_base.cpp文件:

#include g_base.hcv::Mat g_img; // 全局图像
int g_fNum = 0; // 全局图像帧计数器QMutex g_m_reader;
QWaitCondition g_wc_reader;QMutex g_m_proc;
QWaitCondition g_wc_proc;

        .cpp中定义全局变量并初始化,.h中做导出。其它文件引用时,只需加载头文件,而不必担心重复引用问题。

qreader.cpp读取类关键代码:

while(true)
{g_m_reader.lock(); // 加锁,保护下面代码qDebug()<< "QReader run(), tid :" << QThread::currentThreadId();cap.read(g_img);if (g_img.empty()){qDebug() << "===> image empty.";continue;}g_m_reader.unlock(); // 解锁g_wc_reader.wakeAll(); // g_wc_reader信号量唤醒
}

qprocess.cpp视频处理类关键代码:

while(true)
{g_m_proc.lock(); // 加锁,保护下面代码g_wc_reader.wait(&g_m_proc); // 等待视频解码信号量g_wc_reader唤醒qDebug()<< "QProcess run(), tid :" << QThread::currentThreadId();msleep(60);qDebug()<< "process nFrame =" << nFrame;emit processed(g_img, nFrame); // 发送信号。此处未做线程同步,需深拷贝数据g_m_proc.unlock(); // 解锁g_wc_proc.wakeAll();  // g_wc_proc信号量唤醒,本示例未使用
}

manager.cpp管理类关键代码:

// 信号与槽关系,线程开启初始化
QManager::QManager(const std::string& url):m_reader(url)
{connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeOut()));connect(&m_proc, SIGNAL(processed(cv::Mat &, int)), this, SLOT(onImageProcessed(cv::Mat &, int)));m_t1 = new QThread();m_reader.moveToThread(m_t1);QObject::connect(m_t1, &QThread::started, &m_reader, &QReader::run);m_t2 = new QThread();m_proc.moveToThread(m_t2);QObject::connect(m_t2, &QThread::started, &m_proc, &QProcess::run);m_t1->start();m_t2->start();
}// GUI线程显示槽函数
void QManager::onImageProcessed(cv::Mat &img, int ret)
{double t = (double)cv::getTickCount();qDebug()<< "QManager onImageProcessed(), tid :" << QThread::currentThreadId();int nFrame = ret;cv::Mat frame = img;cv::putText(frame, frame  + std::to_string(nFrame), cv::Point(int(200), int(100)),cv::FONT_HERSHEY_PLAIN, 3, cv::Scalar(255, 0, 0), 2, 8, 0);cv::imshow(video, frame);t = ((double)cv::getTickCount() - t) * 1000 / cv::getTickFrequency();qDebug() << "nFrame =" << nFrame << ", Show time for a frame :" << t << "ms.";
}

结果:

 应用二、视频读取类等待处理类处理完毕后再读取下一帧。

qreader.cpp读取类改动部分:

bool is_start = false;
while(true)
{g_m_reader.lock(); // 加锁,保护下面代码if (is_start) // 第一次读取,开启循环,之后等待处理结果后才解码。g_wc_proc.wait(&g_m_reader);elseis_start = true;...

应用三、固定帧率显示图像结果。

        将主线程显示改为Timer定时拿取结果显示。

manager.h头文件定义:

#include <QTimer>
... QTimer m_timer; // 定时器

manager.cpp添加: 

QManager::QManager(const std::string& url):m_reader(url)
{connect(&m_timer, SIGNAL(timeout()), this, SLOT(onTimeOut()));...m_timer.start(40); // 定时器40ms读取一次数据
}// 定时器到时处理槽函数
void QManager::onTimeOut()
{double t = (double)cv::getTickCount();qDebug()<< "QManager onTimeOut(), tid :" << QThread::currentThreadId();int nFrame = g_fNum;cv::Mat frame = g_img;cv::putText(frame, frame  + std::to_string(nFrame), cv::Point(int(200), int(100)),cv::FONT_HERSHEY_PLAIN, 3, cv::Scalar(255, 0, 0), 2, 8, 0);cv::imshow(video, frame);t = ((double)cv::getTickCount() - t) * 1000 / cv::getTickFrequency();qDebug() << "nFrame =" << nFrame << ", Show time for a frame :" << t << "ms.";
}

 传送门:qt多线程系列文章目录 


http://chatgpt.dhexx.cn/article/TWu5kHXT.shtml

相关文章

生产者、消费者模式

架构设计&#xff1a;生产者/消费者模式[0]&#xff1a;概述 今天打算来介绍一下“生产者&#xff0f;消费者模式”&#xff0c;这玩意儿在很多开发领域都能派上用场。由于该模式很重要&#xff0c;打算分几个帖子来介绍。今天这个帖子先来扫盲一 把。如果你对这个模式已经比较…

生产者和消费者模式详解

★简介 生产者消费者模式并不是GOF提出的23种设计模式之一&#xff0c;23种设计模式都是建立在面向对象的基础之上的&#xff0c;但其实面向过程的编程中也有很多高效的编程模式&#xff0c;生产者消费者模式便是其中之一&#xff0c;它是我们编程过程中最常用的一种设计模式。…

生产者消费者模式+代码实现

在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度。 为什么要使用生产者和消费者模式 在线程世界里&#xff0c;生产者就是生产数据的线程&#xff0c;消费者就是消费数据的线程。在…

生产者消费者模式详解(转载)

★简介 在实际的软件开发过程中&#xff0c;经常会碰到如下场景&#xff1a;某个模块负责产生数据&#xff0c;这些数据由另一个模块来负责处理&#xff08;此处的模块是广义的&#xff0c;可以是类、函数、线程、进程等&#xff09;。产生数据的模块&#xff0c;就形象地称为生…

多线程之生产者消费者模式

文章目录 基本组成阻塞队列有界队列与无界队列ArrayBlockingQueueLinkedBlockingQueueSynchronousQueue 流量控制与信号量(Semaphore)双缓冲与Exchanger 基本组成 生产者&#xff1a;生产者的任务是生产产品&#xff0c;产品可以是数据&#xff0c;也可以是任务。(将产品存入传…

java 生产者消费者模式

java的生产者消费者模式&#xff0c;有三个部分组成&#xff0c;一个是生产者&#xff0c;一个是消费者&#xff0c;一个是缓存。 这么做有什么好处呢&#xff1f; 1.解耦(去依赖)&#xff0c;如果是消费者直接调用生产者&#xff0c;那如果生产者的代码变动了&#xff0c;消费…

生产者消费者模式浅析

由于最近工作中&#xff0c;涉及到生产者消费者设计模式&#xff0c;对此有一些体会&#xff0c;所以总结一下&#xff0c;与大家分享。 什么是生产者消费者模式 在工作中&#xff0c;大家可能会碰到这样一种情况&#xff1a;某个模块负责产生数据&#xff0c;这些数据由另一个…

java实现生产者消费者模式

一: 什么是生产者消费者模型 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接扔给阻塞队列&#xff0c;消费…

【Java】生产者消费者模式的实现

前言 生产者消费者问题是线程模型中的经典问题&#xff1a;生产者和消费者在同一时间段内共用同一存储空间&#xff0c;生产者向空间里生产数据&#xff0c;而消费者取走数据。 阻塞队列就相当于一个缓冲区&#xff0c;平衡了生产者和消费者的处理能力。这个阻塞队列就是用来…

生产者/消费者模式

[0]&#xff1a;概述 今天打算来介绍一下“生产者&#xff0f;消费者模式”&#xff0c;这玩意儿在很多开发领域都能派上用场。由于该模式很重要&#xff0c;打算分几个帖子来介绍。今天这个帖子先来扫盲一把。如果你对这个模式已经比较了解&#xff0c;请跳过本扫盲帖&#x…

(四)生产者消费者模式

&#xff08;一)生产者消费者模式原理&#xff1a; 在一个系统中&#xff0c;存在生产者和消费者两种角色&#xff0c;他们通过内存缓冲区进行通信&#xff0c;生产者生产消费者需要的资料&#xff0c;消费者把资料做成产品。生产消费者模式如下图&#xff1a; &#xff08;二…

【C++】【设计模式之】生产者-消费者模型(理论讲解及实现)

一、什么是生产者-消费者模型 1、简单理解生产者-消费者模型 假设有两个进程&#xff08;或线程&#xff09;A、B和一个固定大小的缓冲区&#xff0c;A进程生产数据放入缓冲区&#xff0c;B进程从缓冲区中取出数据进行计算&#xff0c;这就是一个简单的生产者-消费者模型。这里…

设计模式——生产者消费者模式

1 基本概括 2 主要介绍 2.1 概念 生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯&#xff0c;而通过阻塞队列来进行通讯&#xff0c;所以生产者生产完数据之后不用等待消费者处理&#xff0c;直接扔给阻塞队列&#xff…

生产者消费者模式三种实现方式

目录 1.什么是生产者消费者模式&#xff1a;2.生产者消费者模型的实现&#xff1a;第一种&#xff1a;使用 synchronized和wait、notify第二种&#xff1a;使用 Lock和await、signal第三种&#xff1a;使用 阻塞队列 BlockingQueue 1.什么是生产者消费者模式&#xff1a; 生产…

t-SNE算法

t-SNE(t-distributed stochastic neighbor embedding)是用于降维的一种机器学习算法&#xff0c;是由 Laurens van der Maaten 和 Geoffrey Hinton在 08 年提出来。t-SNE 是一种非线性降维算法&#xff0c;非常适用于高维数据降维到 2 维或者 3 维&#xff0c;进行可视化。在实…

t-SNE概述

为了循序渐进, 先来学习SNE. SNE 无论是多维数据还是词向量, 都是一个个散落在空间中的点, 点与点之间距离近的, 就可以看作属于同一分类或近义词. 衡量两点距离有很多种手段, 但最常用的还是欧式距离, 所以欧氏距离与相似度的关系可以用某种公式近似表达, 这样就可以把空间信…

机器学习笔记 - 什么是t-SNE?

1、t-SNE概述 t-Distributed Stochastic Neighbor Embedding (t-SNE) 是一种无监督的非线性技术,主要用于数据探索和高维数据的可视化。简单来说,t-SNE 让您对数据在高维空间中的排列方式有一种感觉或直觉。它由 Laurens van der Maatens 和 Geoffrey Hinton 于 2008 年提出。…

可视化降维方法 t-SNE

本篇主要介绍很好的降维方法t-SNE的原理 详细介绍了困惑度perplexity对有效点的影响首先介绍了SNE然后在SNE的基础上进行改进&#xff1a;1.使用对称式。2.低维空间概率计算使用t分布 t-SNE&#xff08;t分布和SNE的组合&#xff09; 以前的方法有个问题&#xff1a;只考虑相…

t-SNE非线性降维

TSNE&#xff08;t-Distributed Stochastic Neighbor Embedding &#xff09;是对SNE的改进&#xff0c;SNE最早出现在2002年&#xff0c;改变了MDN和ISOMAP中基于距离不变的思想&#xff0c;将高维映射到低维的同时&#xff0c;尽量保证相互之间的分布概率不变&#xff0c;SNE…

t-SNE原理及代码

SNE 基本原理 SNE是通过仿射变换将数据点映射到概率分布上&#xff0c;主要包括两个步骤&#xff1a;  &#xff11;) SNE构建一个高维对象之间的概率分布&#xff0c;使得相似的对象有更高的概率被选择&#xff0c;而不相似的对象有较低的概率被选择。   &#xff12;) SN…