目录
以取像-显示举例说明如下:
1.worker-object模式
核心思想:Worker描述"做什么 ",Thread描述"在哪里做"
工作原理:
- 创建一个继承自
QObject的类(Worker),把所有耗时逻辑写成它的槽函数; - 创建一个普通的
QThread实例; - 使用
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.双缓冲模式
核心思想:空间换时间,消除竞争。使用两块独立的内存区域,实现读与写的完全并行
工作原理:
- 后缓冲区:供写入线程使用,负责接收新数据。
- 前缓冲区:供读取线程使用,负责渲染或分析。
- 交换 :当后台写完且前台读完时,瞬间交换指针,两者始终不在同一块物理内存上争抢。
举例:
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.生产者-消费者模式
核心思想:解耦与缓冲。通过一个中间容器(队列)平衡数据产生和数据处理速度不匹配的问题。
工作原理:
- 生产者线程:负责生成数据(如采集图像)并存入阻塞队列。
- 消费者线程:负责从队列中取出数据并处理(如识别算法)。
- 缓冲区:当队列满时,生产者挂起;当队列空时,消费者挂起。
举例:
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.主从模式
核心思想:分治与汇总。将一个大任务拆分成多个并行的小任务,由多个工人同时完成。
工作原理:
- Master 线程:负责任务的分发、监控和结果汇总。
- Worker 线程:负责执行具体的子任务逻辑。
- 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.管道模式
核心思想:流水线并行。将任务拆分为有序的阶段,每个阶段由独立线程负责,提高整体吞吐量。
工作原理:
- 数据像流水一样经过 A -> B -> C 三个阶段。
- 当阶段 B 在处理第 N 个数据时,阶段 A 已经在处理第 N+1 个数据。
- 类似于 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); }); //识别