QtC++使用QRunnable+QThreadPool管理多线程

本文的示例项目工程在文章最后有分享链接,可以下载运行试试。下载后替换成自己的Qt版本即可(项目 -> 属性 -> 配置属性 -> Qt Project Settings -> Qt Installation)

应用场景介绍

现有一个应用场景,需要进行上千个循环里面,调用相同的功能类去计算,只使用一个线程去计算,效率太低,结合网络上的帖子和自身实践。我的实现场景如下:

  1. 界面类Dialog。给用户提供交互操作
  2. 计算类Logic。执行用户点击后的计算逻辑
  3. 任务类QRunnable。在若干循环里面用于完成指定任务

运行过程

在Qt界面中,用户点击某个控件后,创建一个线程去执行计算操作,避免界面阻塞,陷入卡死。
(这里有个不继承 qthread 使用多线程的方法,提供另一种实现方法思路)

	QThread*   mThread;    //!< 计算类执行的线程
	Calculate* mCalculate; //!< 计算类,放在mThread线程里面执行,避免界面卡死
	
	/************************************************************************/
	/*                            上面为成员变量                              */
	/************************************************************************/ 
	
	mThread    = new QThread();
	mCalculate = new Calculate();
	
	mCalculate->moveToThread(mThread); // 改变mCalculate的线程依附关系,将计算类放在线程中执行
	
	//! 释放堆空间资源,避免内存泄露
	connect(mThread, &QThread::finished, mThread, &QObject::deleteLater);
	connect(mThread, &QThread::finished, mCalculate, &QObject::deleteLater);
	
	//! 注意:在使用跨线程通信时,参数需要为元数据类型(具体我也解释不清楚,理解是有关 meta 什么的)
	qRegisterMetaType<CalculateInputStruct>("CalculateInputStruct"); // 将结构体 CalculateInputStruct 注册为元数据类型
	
	//! 连接其他信号槽,用于触发线程执行槽函数里的任务    
	connect(this, &MultiThreadDemo::StartCalculate, mCalculate, &Calculate::startCalculateSlot, Qt::QueuedConnection);         // 默认使用Qt::QueuedConnection,保证槽函数的执行顺序
	connect(mCalculate, &Calculate::CalculateFinished, this, &MultiThreadDemo::calculate_finished_slot, Qt::QueuedConnection); // 计算完成显示信息
	connect(mCalculate, &Calculate::UpdateProssorbar, this, &MultiThreadDemo::update_prossorbar_slot, Qt::QueuedConnection);   // 每计算一次完一次任务,更新界面进度条
	
	mThread->start(); // 启动线程,线程默认开启事件循环,并且线程正处于事件循环状态

类似Run函数实现

计算类使用 qthreadpool 线程池实现,这里的任务是独立的,每个计算任务仅使用传递的参数,没有涉及资源共享

void Calculate::startCalculateSlot(const CalculateInputStruct aInput)
{
	auto test_thread_id = QThread::currentThreadId(); // 查看当前线程的id和界面类、任务类的id是否相同

	QThreadPool thread_pool;
	thread_pool.setMaxThreadCount(aInput.threadMaxCount); // 设置线程池的最大线程数为1
	for (int i = 0; i < aInput.calculateCount; i++)
	{
		CalculateRunnable* runnable = new CalculateRunnable;
		runnable->setAutoDelete(true); // 设置任务结束后自动删除
		thread_pool.start(runnable);

		//! 设置响应方式为消息队列时,会在emit CalculateFinished(); 后再响应,故这里使用直接响应
		connect(runnable, &CalculateRunnable::RunnableFinished, this, [&]() {emit UpdateProssorbar(); }, Qt::DirectConnection); 		
	}
	thread_pool.waitForDone(); // 等待所有任务完成
	emit CalculateFinished();
}
}

结果对比

一个任务类在run函数中的计算为0.5s。可以看出,理论上线程池中还是有效的。

在这里插入图片描述

参考

在寻找答案的过程中,看了很多帖子,分享几遍个人觉得质量不错的文章

  1. Qt多线程通信
  2. Qt线程之QRunnable的使用详解
  3. QT 多线程的实现方法以及GUI线程与其他线程间的通信
  4. 【QT】子类化QObject+moveToThread实现多线程
  5. Qt使用多线程的一些心得——1.继承QThread的多线程使用方法

工程示例

  1. 开发环境:Qt5.15.2 + vs2022
  2. csdn链接:https://download.youkuaiyun.com/download/weixin_51238542/90291812
  3. 百度网盘链接:https://pan.baidu.com/s/1JkKm6QF1TrFbQbgyaPqZSw?pwd=tvhn
  4. github链接:https://github.com/tianxingithub/MultiThreadDemo
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值