多线程并发处理模式详解

目录

1.worker-object模式

2.双缓冲模式

3.生产者-消费者模式

4.主从模式

5.管道模式


以取像-显示举例说明如下:

1.worker-object模式

核心思想:Worker描述"做什么 ",Thread描述"在哪里做"

工作原理:

  1. 创建一个继承自 QObject 的类(Worker),把所有耗时逻辑写成它的槽函数;
  2. 创建一个普通的 QThread 实例;
  3. 使用 worker->moveToThread(thread),此时 Worker 的所有槽函数都会在子线程中执行。

举例:

cpp 复制代码
//Worker类:处理逻辑
class CameraWorker : public QObject 
{
    Q_OBJECT
public slots:
    void doGrab() 
    {
        while (m_running) 
        {
            cv::Mat frame = m_cam.read(); 
            emit sigNewFrame(frame);    
        }
    }
private:
    bool m_running = true;
};

//Thread决定在哪里做
void ImgPreview::start() 
{
    QThread* th = new QThread;
    CameraWorker* worker = new CameraWorker;
    worker->moveToThread(th); // 核心:移交执行权
    connect(th, &QThread::started, worker, &CameraWorker::doGrab);
    th->start();
}

2.双缓冲模式

核心思想:空间换时间,消除竞争。使用两块独立的内存区域,实现读与写的完全并行

工作原理:

  1. 后缓冲区:供写入线程使用,负责接收新数据。
  2. 前缓冲区:供读取线程使用,负责渲染或分析。
  3. 交换 :当后台写完且前台读完时,瞬间交换指针,两者始终不在同一块物理内存上争抢。

举例:

cpp 复制代码
class DoubleBuffer 
{
    cv::Mat buffers[2];
    int writeIdx = 0;	//写入和读取的下标是动态变化的
    QMutex mutex;

public:
    void update(cv::Mat& newFrame) 
	{
        // 相机写入"后方"缓冲区
        newFrame.copyTo(buffers[writeIdx]);	//向0写入(copy操作在锁外,效率更高)
        QMutexLocker locker(&mutex);
        writeIdx = !writeIdx; // 变更索引,下次向变更后的索引1中写入图像
    }
    cv::Mat getForDisplay() 
	{
        QMutexLocker locker(&mutex);
        return buffers[!writeIdx]; // UI读取"前方"缓冲区(读取0的图像)
    }
};

3.生产者-消费者模式

核心思想:解耦与缓冲。通过一个中间容器(队列)平衡数据产生和数据处理速度不匹配的问题。

工作原理:

  1. 生产者线程:负责生成数据(如采集图像)并存入阻塞队列。
  2. 消费者线程:负责从队列中取出数据并处理(如识别算法)。
  3. 缓冲区:当队列满时,生产者挂起;当队列空时,消费者挂起。

举例:

cpp 复制代码
QQueue<cv::Mat> imageQueue;
QWaitCondition condition;
QMutex mutex;

// 生产者线程(相机)
void produce() 
{
    while(1) 
	{
        cv::Mat img = cam.grab();
        mutex.lock();
        imageQueue.enqueue(img); // 存入图库
        condition.wakeOne();     // 唤醒等图的算法线程
        mutex.unlock();
    }
}

// 消费者线程(算法/存盘)
void consume() {
    while(1) {
        mutex.lock();
        while(imageQueue.isEmpty())
		{
			condition.wait(&mutex); // 没图,挂起消费者
		}
        cv::Mat img = imageQueue.dequeue();
        mutex.unlock();
        process(img); // 执行耗时算法
    }
}

4.主从模式

核心思想:分治与汇总。将一个大任务拆分成多个并行的小任务,由多个工人同时完成。

工作原理:

  1. Master 线程:负责任务的分发、监控和结果汇总。
  2. Worker 线程:负责执行具体的子任务逻辑。
  3. Master 将任务丢进任务池,Workers 竞争或按需领取任务。
cpp 复制代码
//拿到一张大图后,分发给多个线程同时检测不同区域(如OCR、划痕、尺寸)
void MasterProcess(cv::Mat fullImage) 
{
    // Master 负责拆解任务
    QList<cv::Rect> regions = {rect1, rect2, rect3};
    
    // 分发给多个 Worker 并行执行
    QtConcurrent::blockingMap(regions, [fullImage](const cv::Rect& r){
        cv::Mat subImg = fullImage(r);
        RunAlgorithm(subImg); // 多个核心同时跑不同的区域
    });
    
    // 所有 Worker 完成后,Master 汇总结果显示
    updateUI();
}

5.管道模式

核心思想:流水线并行。将任务拆分为有序的阶段,每个阶段由独立线程负责,提高整体吞吐量。

工作原理:

  1. 数据像流水一样经过 A -> B -> C 三个阶段。
  2. 当阶段 B 在处理第 N 个数据时,阶段 A 已经在处理第 N+1 个数据。
  3. 类似于 CPU 的指令流水线。
cpp 复制代码
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>

template <typename T>
class SafeQueue 
{
    QQueue<T> queue;
    QMutex mutex;
    QWaitCondition condition;
    int maxSize = 5; // 限制大小,防止处理太慢导致内存爆炸

public:
    void push(T value) 
	{
        QMutexLocker locker(&mutex);
        if (queue.size() >= maxSize) 
		{
			queue.dequeue(); // 丢弃最旧的图(可选)
		}
        queue.enqueue(value);
        condition.wakeOne();
    }

    T pop() 
	{
        QMutexLocker locker(&mutex);
        while (queue.isEmpty()) 
		{
			condition.wait(&mutex); // 没数据就阻塞休眠
		}
        return queue.dequeue();
    }
};


//取像
void stageGrab(SafeQueue<cv::Mat>& outQueue) 
{
    while (true) 
	{
        cv::Mat frame = captureFromCamera(); // 模拟硬件取像
        outQueue.push(frame);
        QThread::msleep(30); // 模拟 33 FPS
    }
}

//预处理
void stagePreProcess(SafeQueue<cv::Mat>& inQueue, SafeQueue<cv::Mat>& outQueue) 
{
    while (true) 
	{
        cv::Mat frame = inQueue.pop(); // 阻塞等待 Stage A 的图
        
        // 执行耗时的图像操作
        cv::GaussianBlur(frame, frame, cv::Size(5, 5), 0);
        cv::cvtColor(frame, frame, cv::COLOR_BGR2GRAY);
        
        outQueue.push(frame); // 传给 Stage C
    }
}

//识别
void stageIdentify(SafeQueue<cv::Mat>& inQueue)
 {
    while (true) 
	{
        cv::Mat frame = inQueue.pop(); // 阻塞等待 Stage B 的图
        
        // 执行复杂的识别逻辑
        std::string result = doOCR(frame); 
        qDebug() << "识别结果:" << QString::fromStdString(result);
    }
}

// 1. 定义两个中转管道
SafeQueue<cv::Mat>* queueA = new SafeQueue<cv::Mat>();	//取像队列
SafeQueue<cv::Mat>* queueB = new SafeQueue<cv::Mat>();	//预处理后(识别)的图像队列

// 2. 启动所有工位(在不同线程运行)
QtConcurrent::run([=]() { stageGrab(*queueA); });					//取像
QtConcurrent::run([=]() { stagePreProcess(*queueA, *queueB); });	//预处理
QtConcurrent::run([=]() { stageIdentify(*queueB); });				//识别
相关推荐
Alter12302 小时前
海南椰子鸡和宁夏滩羊的拼多多“漂流”:透视地域特产的数字进化论
大数据·人工智能
柠萌f2 小时前
《当投放预算跑不动:易元AI如何解决电商素材产能的系统性瓶颈》
人工智能
小陈phd2 小时前
langGraph从入门到精通(七)——基于 LangGraph 的结构化数据AI 代理自动入库实战
人工智能
汽车仪器仪表相关领域2 小时前
全组分精准捕获,台架研发中枢:MEXA-ONE发动机尾气测量装置项目实战全景
大数据·人工智能·功能测试·单元测试·压力测试·可用性测试
(; ̄ェ ̄)。2 小时前
机器学习入门(八)过拟合、欠拟合、L1、L2正则化
人工智能·机器学习
qyresearch_2 小时前
全球干燥水果和蔬菜市场:健康消费浪潮下的增长引擎与产业重构
大数据·人工智能·物联网
没学上了2 小时前
Vlm-RT-DETR网络模型部署推理
人工智能
LJ97951112 小时前
媒体发布+时代:Infoseek如何用AI重构你的内容分发网络
大数据·人工智能
GISer_Jing2 小时前
2026前端技术潜在主流前沿方向
前端·人工智能·reactjs