C++ 一个简明的多线程存图队列

目录

前言:

1. 源码

2. 结构体设计

3. 多线程设计

结语:


前言:

        本文基于C++11提供一个简单明了的“多线程存图队列”的模板,使用opencv处理图像,结合线程池调用,当然不熟悉线程池下面也给出了替代方案,C++11 thread创建单个子线程。文末也提供了相关知识的补充文章。

        我们也可以基于结构体提供更多信息,增加更多对图像的处理操作。咱们还是先上源码再讲设计,建议结合设计再看代码哦。

1. 源码

        SaveImgWorker.h

// SaveImgWorker.h
#pragma once

// opencv操作图像数据
#include "opencv2/opencv.hpp"

#include <string>
#include <mutex>
#include <queue>

/**
 * 线程池.
 * 也可以使用C11 thread()开辟线程替换线程池,但这种便会频繁开辟释放线程,耗能增大..
 */
class ThreadPool;

typedef struct _ImageInfo
{
	cv::Mat image;
	// double scale;
	// string fileName;
	// ...
}ImageInfo;


class SaveImgWorker
{
public:
	SaveImgWorker();
	~SaveImgWorker();
public:
	/**
	 * 线程调用接口.
	 * 静态函数不用传入this指针,适合异步调用.
	 */
	static void runSaving(void* pUser, void* args);

	/**
	 * 图像入队.
	 */
	void enQueue(const ImageInfo& info);

	/**
	 * 存图操作.
	 */
	void run();


	//=====================启动子线程前的操作=================//
	/**
	 * 准备操作,设置存图路径.
	 */
	bool setPath(std::string path);

	/**
	 * 准备操作,重置队列.
	 */
	void reset();

	/**
	 * 准备操作,初始化线程池索引.
	 */
	void initThreadPool(ThreadPool* threadPool);

protected:
	std::mutex m_mutexRuning;// 状态锁
	std::mutex m_mutexQueue;// 队列锁
	std::queue<ImageInfo> m_ImageInfoQueue;
	std::string m_sFilePath;// 存图地址
	uint64_t m_nSaveNum;// 累计存图数

	bool m_bIsRunning;// 存储线程运行状态,同时只能有一个线程进行存图
	ThreadPool* m_pThreadPool;
};

        SaveImgWorker.cpp

// SaveImgWorker.cpp

#include "SaveImgWorker.h"
#include "ThreadPool.h"// 未提供线程池代码
#include <thread>

using namespace std;

SaveImgWorker::SaveImgWorker()
{
	m_pThreadPool = nullptr;
	m_bIsRunning = false;
}

SaveImgWorker::~SaveImgWorker()
{
	while (!m_ImageInfoQueue.empty())
		m_ImageInfoQueue.pop();

	m_pThreadPool = nullptr;
}


void SaveImgWorker::runSaving(void* pUser, void* args)
{
	SaveImgWorker* saveWorker = (SaveImgWorker*)pUser;
	if (saveWorker != nullptr)
		saveWorker->run();
}

// 通常是主线程执行入队操作
void SaveImgWorker::enQueue(const ImageInfo& info)
{
	m_mutexQueue.lock();
	m_ImageInfoQueue.push(info);
	m_mutexQueue.unlock();

	unique_lock<mutex> lock(m_mutexRuning);
	// 若未在存图中,则启动子线程执行
	if (!m_bIsRunning && m_pThreadPool != nullptr)
	{
		lock.unlock();
		// 线程池执行存图操作
		m_pThreadPool->pushJob(runSaving, this, nullptr, 0);

		// 不了解线程池也可以如下替换,创建单个线程:
		//thread saveJob(runSaving, this, nullptr);
		//saveJob.detach();
	}
}


// 存图操作,只能同时占用一个线程
void SaveImgWorker::run()
{
	m_mutexRuning.lock();
	m_bIsRunning = true;
	m_mutexRuning.unlock();

	cv::Mat mat;
	ImageInfo imageInfo;

	unique_lock<mutex> lock(m_mutexQueue);
	while (!m_ImageInfoQueue.empty())
	{
		lock.unlock();

		// 已判断非空,入队出队不冲突
		imageInfo = m_ImageInfoQueue.front();
		m_ImageInfoQueue.pop();
		mat = imageInfo.image;

		// 存图及图像处理操作
		if (m_sFilePath != "")
			cv::imwrite((m_sFilePath + std::to_string(m_nSaveNum++) + ".bmp"), mat);

		lock.lock();
	}

	m_mutexRuning.lock();
	m_bIsRunning = false;
	m_mutexRuning.unlock();
}

bool SaveImgWorker::setPath(string path)
{// 判定文件夹是否存在

	if (true)
	{
		m_sFilePath = path + "/image";
		return true;
	}
	return false;
}

void SaveImgWorker::reset()
{
	m_nSaveNum = 0;
	while (!m_ImageInfoQueue.empty())
		m_ImageInfoQueue.pop();
}


void SaveImgWorker::initThreadPool(ThreadPool* threadPool)
{
	m_pThreadPool = threadPool;
}

2. 结构体设计

        入队的数据为包含图像数据的结构体ImageInfo,要处理更多的图像操作,当然要提供更多的数据,例如缩放比例,文件名......

typedef struct _ImageInfo
{
	cv::Mat image;
	// double scale;
	// string fileName;
	// ...
}ImageInfo;

        注意,cv::Mat image 实际只存储了输入图像的地址,并未对图像数据进行深拷贝

3. 多线程设计

        入队操作enQueue()通常是在主线程进行的,而存图操作run()则是在线程池ThreadPool或子线程saveJob进行。同时通过enQueue管理存图任务只交代给一个线程去处理,要注意加锁。实际上,这里主线程入队操作是很快的,因为并没有拷贝图像,而子线程存图是极为耗时的。

结语:

补充相关知识

QT 一个简明的多线程存图队列_qt队列显示图片-CSDN博客

Visual Stdio安装并配置OpenCV_vs studio如何配置opengl和opencv-CSDN博客

一文详解C++多线程-CSDN博客

C/C++手撕线程池(线程池的封装和实现)_c++线程池-CSDN博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值