【记录】C++生产者 / 消费者 案例

一、实现流程

1. 定义线程安全的队列结构

需要封装std::queue,用std::mutex保证互斥访问,用std::condition_variable触发唤醒:

cpp 复制代码
#include <queue>
#include <mutex>
#include <condition_variable>
#include <opencv2/opencv.hpp>  // 假设用OpenCV存储图像

// 线程安全的图像队列
class ImageQueue {
private:
    std::queue<cv::Mat> queue_;       // 存储图像数据
    std::mutex mtx_;                  // 互斥锁,保护队列操作
    std::condition_variable cv_;      // 条件变量,用于线程同步
    bool is_stopped_ = false;         // 退出标志(用于优雅终止线程)

public:
    // 生产者调用:放入图像
    void push(const cv::Mat& img) {
        std::lock_guard<std::mutex> lock(mtx_);  // 自动加锁/解锁
        queue_.push(img);
        cv_.notify_one();  // 通知等待的消费者(唤醒一个线程)
    }

    // 消费者调用:取出图像(无数据时阻塞)
    bool pop(cv::Mat& img) {
        std::unique_lock<std::mutex> lock(mtx_);  // 可手动解锁的锁
        // 等待条件:队列非空 或 线程需终止
        cv_.wait(lock, [this]{ return !queue_.empty() || is_stopped_; });
        
        if (is_stopped_) return false;  // 线程终止,退出
        
        img = queue_.front();
        queue_.pop();
        return true;
    }

    // 终止线程时调用
    void stop() {
        std::lock_guard<std::mutex> lock(mtx_);
        is_stopped_ = true;
        cv_.notify_all();  // 唤醒所有等待的线程
    }
};
2. 实现生产者线程(相机采集)

生产者的核心是快速获取图像并放入队列,避免在回调 / 采集函数中做耗时操作:

cpp 复制代码
#include <thread>
#include <相机SDK头文件>  // 替换为实际相机SDK

ImageQueue img_queue;  // 全局队列(或通过指针传递)
bool is_running = true;

// 相机回调函数(生产者的核心逻辑)
void cameraCallback(const CameraImage& raw_img) {
    if (!is_running) return;
    
    // 轻量操作:将原始图像转为OpenCV格式(快速转换,避免复杂计算)
    cv::Mat img(raw_img.height, raw_img.width, CV_8UC3, raw_img.data);
    
    // 放入队列(耗时极短)
    img_queue.push(img);
}

// 启动相机采集线程
void startProducer() {
    // 初始化相机(根据SDK文档配置)
    Camera::init();
    Camera::setCallback(cameraCallback);  // 设置回调
    Camera::startCapture();  // 开始采集(如30fps)
    
    // 保持线程运行(或由SDK内部线程驱动)
    while (is_running) {
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
    }
    
    Camera::stopCapture();
    Camera::release();
}
3. 实现消费者线程(图像处理)

消费者从队列中取图并处理,处理速度慢也不会阻塞生产者(队列会暂存数据):

cpp 复制代码
// 图像处理函数(耗时操作)
void processImage(const cv::Mat& img) {
    cv::Mat gray;
    cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);  // 示例:转灰度
    cv::GaussianBlur(gray, gray, cv::Size(5,5), 0);  // 高斯模糊
    // ... 其他耗时操作(如目标检测、特征提取等)
}

// 消费者线程
void startConsumer() {
    cv::Mat img;
    while (img_queue.pop(img)) {  // 无数据时阻塞,不占用CPU
        processImage(img);  // 处理图像(耗时操作)
    }
}
4. 主线程控制启动与退出
cpp 复制代码
int main() {
    // 启动生产者和消费者线程
    std::thread producer_thread(startProducer);
    std::thread consumer_thread(startConsumer);
    
    // 运行一段时间(或等待用户输入退出)
    std::cout << "按Enter键退出..." << std::endl;
    std::cin.get();
    
    // 终止流程
    is_running = false;          // 停止生产者
    img_queue.stop();            // 通知消费者退出
    producer_thread.join();      // 等待生产者线程结束
    consumer_thread.join();      // 等待消费者线程结束
    
    return 0;
}

二、关键优势

  1. 避免 CPU 空转 :消费者无数据时通过cv.wait()进入休眠,不占用 CPU;生产者放入数据后通过cv.notify_one()唤醒,仅在必要时工作。

  2. 解耦采集与处理:相机采集(快)和图像处理(慢)独立运行,采集线程不会被处理耗时阻塞,避免丢帧;处理线程也不会因采集速度快而超负荷。

  3. 灵活扩展 :可根据需求增加多个消费者线程(如 2 个处理线程并行处理队列数据),充分利用多核 CPU:

    cpp 复制代码
    // 启动多个消费者线程
    std::vector<std::thread> consumers;
    for (int i = 0; i < 2; ++i) {
        consumers.emplace_back(startConsumer);
    }

三、注意事项

  1. 队列长度控制 :若处理速度长期慢于采集速度,队列会无限增长导致内存溢出。可在push时增加限制:

    cpp 复制代码
    void push(const cv::Mat& img) {
        std::lock_guard<std::mutex> lock(mtx_);
        if (queue_.size() > 10) {  // 限制最大缓存10帧
            queue_.pop();  // 丢弃最旧的帧
        }
        queue_.push(img);
        cv_.notify_one();
    }
  2. 图像数据拷贝 :若图像数据较大,push时的cv::Mat拷贝(浅拷贝,仅复制头信息)是高效的,但需确保原始数据生命周期足够长(避免相机 SDK 过早释放)。

  3. 线程安全:所有对队列的操作必须加锁,否则可能导致队列数据混乱或崩溃。

相关推荐
PEI049 分钟前
MVCC(多版本并发控制)
java·开发语言·数据库
熊猫钓鱼>_>33 分钟前
2025反爬虫之战札记:从robots.txt到多层防御的攻防进化史
开发语言·c++·爬虫
oscar9991 小时前
少儿编程C++快速教程之——1. 基础语法和输入输出
c++·基础语法·少儿编程
牛十二1 小时前
mac-intel操作系统go-stock项目(股票分析工具)安装与配置指南
开发语言·前端·javascript
励志不掉头发的内向程序员1 小时前
从零开始的python学习——文件
开发语言·python·学习
恒森宇电子有限公司1 小时前
IP5326_BZ 支持C同口输入输出的移动电源芯片 2.4A的充放电电流 支持4LED指示灯
c语言·开发语言·单片机
曼巴UE52 小时前
UE5.3 C++ 接口初步使用
开发语言·jvm·c++
奔跑的石头_2 小时前
GO语言的主要语法和特性
开发语言
泛联新安2 小时前
如何根据项目需求选择合适的软件测试工具?iUnit智能单元测试平台提供专业化解决方案
c++·测试工具·单元测试
曙曙学编程2 小时前
stm32——NVIC,EXIT
c语言·c++·stm32·单片机·嵌入式硬件