Qt中多线程使用案列

Qt中多线程下载大文件

#pragma once

#include <QWidget>
#include <QPushButton>
#include "ThreadPool.h"
#include <QProgressBar>
#include <QLabel>
#include <QHBoxLayout>
#include <QVBoxLayout>
class MainWindow : public QWidget
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = Q_NULLPTR);

private:
   
	void initUI();

	void initConnect();

private:


	QPushButton*  m_dlBtn;
	DownLoad::ThreadPool  threadPool;
	QMap<QString ,std::pair<QLabel*,QProgressBar*> > controlMap;

};

#include "MainWindow.h"

#include "Task.h"
#include "ThreadPool.h"
MainWindow::MainWindow(QWidget *parent)
	: QWidget(parent)
{
	initUI();
	initConnect();
}

void MainWindow::initUI()
{
	m_dlBtn = new QPushButton(this);
	m_dlBtn->setText(QString("DownLoad"));
	QVBoxLayout*  layout = new QVBoxLayout();
	layout->setSpacing(10);
	layout->setContentsMargins(10, 10, 10, 10);
	layout->addWidget(m_dlBtn);
	


	for (int i = 0; i < 10; i++)
	{
		DownLoad::Task* task = new DownLoad::Task("http://mirrors.tuna.tsinghua.edu.cn/archlinux/iso/2023.12.01/archlinux-2023.12.01-x86_64.iso", QString("C:/Users/gd09861-hlw/Desktop/11111/archlinux-2023.12.01-x86_64_%1.iso").arg(i), DownLoad::Task::WorkModel::DOWNLOAD);
		threadPool.push(task);
		QLabel *label = new QLabel(this);
		label->setText(QString("%1").arg(i));
		QProgressBar *progressBar = new QProgressBar(this);
		controlMap.insert(task->id(), std::make_pair(label, progressBar));

		QHBoxLayout *hLayout = new QHBoxLayout;
		hLayout->addWidget(label);
		hLayout->addWidget(progressBar);
		layout->addLayout(hLayout);
	}
	this->setLayout(layout);
}

void MainWindow::initConnect()
{
	connect(m_dlBtn, &QPushButton::clicked, [&]() {
			
			threadPool.startAll();
		

		}


	);
	connect(&threadPool, &DownLoad::ThreadPool::sigUpdateTaskProgress, this, [&](QString id, qint64 bytesR, qint64 bytesT) {

		controlMap[id].second->setValue((bytesR*100.0f) / (bytesT*1.0f));
	
	});

	connect(&threadPool, &DownLoad::ThreadPool::sigUpdateTaskState, this, [&](QString id,DownLoad::Task::State state) {
		switch (state)
		{
		case DownLoad::Task::Start:
		{
			controlMap[id].second->setValue(0);
		}
			break;
		case DownLoad::Task::Stop: {
			controlMap[id].first->setText("Stop");
		}
			break;
		case DownLoad::Task::Finish:
			controlMap[id].first->setText("Finish");
			break;
		case DownLoad::Task::Error:
			controlMap[id].first->setText("error");
			break;
		default:
			break;
		}
	});
}

#ifndef  __TASK_QUEUE_H__
#define  __TASK_QUEUE_H__

#include <QObject>
#include <QString>
#include "Task.h"
#include <QQueue>
#include <QMutex>
namespace DownLoad {

#define  DEFAULT_THREAD_MAX_COUNT  3
	class ThreadPool :public QObject
	{
		Q_OBJECT
	public:


		ThreadPool();
		~ThreadPool();

		void init();
		void  push(Task *task);
		Task* pop();
		void startAll();
		void slotUpdateTaskState(QString id, Task::State  state);
		signals :
				void sigUpdateTaskProgress(QString id, qint64 bytesReceived, qint64 bytesTotal);
				void sigUpdateTaskState(QString id, Task::State  state);
	private:

		QQueue<Task*>  m_tasks;
	
		QList<QThread*>  m_threads;
	};
};
#endif
#include "ThreadPool.h"
#include <QMutexLocker>
#include <QThread>
#include "Task.h"
DownLoad::ThreadPool::ThreadPool()
{
	init();
}

DownLoad::ThreadPool::~ThreadPool()
{

}

void DownLoad::ThreadPool::init()
{
	for (int i = 0; i < DEFAULT_THREAD_MAX_COUNT; i++) {
		QThread  *thread = new QThread();
		m_threads.push_back(thread);
	}
}


void DownLoad::ThreadPool::push(Task *task)
{
	
	m_tasks.enqueue(task);
}

DownLoad::Task* DownLoad::ThreadPool::pop()
{
	
	return m_tasks.dequeue();
}

void DownLoad::ThreadPool::startAll()
{
	if (m_threads.isEmpty()) {
		return;
	}

	for (int i = 0; i < m_threads.count(); i++)
	{
		QThread *  thread = m_threads.at(i);
		if (thread->isRunning()) {
			continue;
		}
		if (m_tasks.isEmpty()) {
			return;
		}
		Task* task = pop();
		task->moveToThread(thread);
	
		connect(task, &Task::sigUpdateProgress, this, &ThreadPool::sigUpdateTaskProgress, Qt::QueuedConnection);
		connect(task, &Task::sigUpdateState, this, &ThreadPool::slotUpdateTaskState, Qt::QueuedConnection);
		connect(thread, &QThread::started, task, &Task::slotDoWork,Qt::QueuedConnection);
		connect(thread, &QThread::finished, task, &Task::deleteLater);
		thread->start();

	}
}

void DownLoad::ThreadPool::slotUpdateTaskState(QString id, Task::State  state)
{
	emit  sigUpdateTaskState(id, state);

	if (state == DownLoad::Task::Finish || state == DownLoad::Task::Error||state==DownLoad::Task::Stop) {
	
		startAll();
	}
}



#ifndef  __TASK_H__
#define  __TASK_H__

#include <QObject>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QSharedPointer>
#include <QUrl>
#include <QString>
#include <QEventLoop>
#include <QMetaType>
namespace  DownLoad {
#define  DOWNLOAD_FILE_SUFFIX  ".tmp"

	

	class Task :public QObject
	{
		Q_OBJECT
	public:
			enum WorkModel
		{
			UPLOAD,
			DOWNLOAD,
		};

		enum State
		{
			Start,
			Stop,
			Finish,
			Error,
		};
	
		QString  id();
		Task(const QString &strUrl, const QString &filePath, const WorkModel& workModel);
		~Task();
		void setSupportBreakPoint(bool isSupport);
		QString lastError();
	signals:


		void sigUpdateState(QString id,State  state);
		void sigUpdateProgress(QString id,qint64 bytesReceived, qint64 bytesTotal);
		public slots:

		void slotDoWork();

		void slotStopWork();

		void slotCancelWork();


	protected:
		void removeTmpFile(const QString &filePath);
		void slotUpdateProgress(qint64 bytesReceived, qint64 bytesTotal);
		void slotWriteFile();
		void slotFinish();
		void slotError(QNetworkReply::NetworkError code);
	protected:
		void doDownWork();
		void doUploadWork();
	private:


		QNetworkReply*				 m_reply = nullptr;
		QNetworkRequest				 m_request;
		QNetworkAccessManager		 m_manager;
		QUrl m_url;
		QString m_filePath = "";
		WorkModel  m_workModel = UPLOAD;
		QSharedPointer<QEventLoop>  m_loop;
		bool m_bSupportBPoint = false;
		qint64  m_bytesReceived;
		qint64  m_bytesTotal;
		qint64  m_bytesCurrentReceived;
		QString m_error = "";
		QString m_id = "";
	};
	
};
Q_DECLARE_METATYPE(DownLoad::Task::State);

#endif     //__TASK_H__
#include "Task.h"
#include <QFileInfo>
#include <QUuid>
#include <QDebug>
#include <QThread>
#include <QDir>


QString DownLoad::Task::id()
{
	return m_id;
}

DownLoad::Task::Task(const QString &strUrl, const QString &filePath, const WorkModel& workModel)
	:QObject(nullptr), m_url(strUrl), m_filePath(filePath), m_workModel(workModel),m_bytesTotal(0)
	,m_bytesReceived(0),m_bytesCurrentReceived(0),m_bSupportBPoint(false),m_id(QUuid::createUuid().toString())
{

}



DownLoad::Task::~Task()
{
	if (m_reply) {
		m_reply->deleteLater();
	}

}



void DownLoad::Task::setSupportBreakPoint(bool isSupport)
{
	m_bSupportBPoint = isSupport;
}

QString DownLoad::Task::lastError()
{
	return m_error;
}

void DownLoad::Task::slotDoWork()
{
	qDebug() << "UUID:" << m_id << "TID:" << QThread::currentThreadId()<<"\t"<<m_filePath;
	if (m_url.isEmpty() || m_filePath.isEmpty()) {
		return;
	}
	switch (m_workModel)
	{
	case DownLoad::Task::UPLOAD:
		doUploadWork();
		break;
	case DownLoad::Task::DOWNLOAD:
		doDownWork();
		break;
	default:
		break;
	}
}



void DownLoad::Task::slotStopWork()
{
	m_bytesCurrentReceived += m_bytesReceived;
	if (m_reply) {
		disconnect(m_reply, 0, this, 0);
		m_reply->abort();
		m_reply->deleteLater();
		m_reply = nullptr;
		this->thread()->exit();
		emit  sigUpdateState(m_id,State::Stop);
	}
}

void DownLoad::Task::slotCancelWork()
{
	slotStopWork();
	m_bytesCurrentReceived = 0;
	m_bytesReceived = 0;
	m_bytesTotal = 0;
	removeTmpFile(m_filePath + DOWNLOAD_FILE_SUFFIX);
}

void DownLoad::Task::removeTmpFile(const QString &filePath)
{
	QFileInfo fileInfo(filePath);
	if (fileInfo.exists()) {
		QFile::remove(filePath);
	}
}

void DownLoad::Task::slotUpdateProgress(qint64 bytesReceived, qint64 bytesTotal)
{
	m_bytesReceived = bytesReceived;
	m_bytesTotal = bytesTotal;
	emit  sigUpdateProgress(m_id,m_bytesReceived + m_bytesCurrentReceived, m_bytesTotal + m_bytesCurrentReceived);
}

void DownLoad::Task::slotWriteFile()
{
	QFile file(m_filePath + DOWNLOAD_FILE_SUFFIX);
	QDir dir=QFileInfo(m_filePath + DOWNLOAD_FILE_SUFFIX).absoluteDir();
	if (!dir.exists()) {
		dir.mkpath(dir.absolutePath());
	}
	if (file.open(QIODevice::WriteOnly | QIODevice::Append)) {
		file.write(m_reply->readAll());
	}
	file.close();
}

void DownLoad::Task::slotFinish()
{
	QVariant  code = m_reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
	qDebug() << "Error Code:" << code.toInt();
	if (m_reply->error() == QNetworkReply::NoError) {
		QFileInfo fileInfo(m_filePath + DOWNLOAD_FILE_SUFFIX);
		if (fileInfo.exists()) {
			QFile::rename(m_filePath+DOWNLOAD_FILE_SUFFIX,m_filePath);
			this->thread()->exit();
			emit sigUpdateState(m_id, State::Finish);
		}
	}
	else { 
		m_error = m_reply->errorString();
		this->thread()->exit();
		emit sigUpdateState(m_id, State::Error);
	}
}

void DownLoad::Task::slotError(QNetworkReply::NetworkError code)
{
	if (code == QNetworkReply::NoError)
		return;
	slotStopWork();
	removeTmpFile(m_filePath + DOWNLOAD_FILE_SUFFIX);
	this->thread()->exit();
	emit	 sigUpdateState(m_id, State::Error);
	m_error = m_reply->errorString();
	

}

void DownLoad::Task::doDownWork()
{
	
	if (m_bytesCurrentReceived <= 0) {
		removeTmpFile(m_filePath + DOWNLOAD_FILE_SUFFIX);
	}
	QFileInfo  fileInfo(m_filePath + DOWNLOAD_FILE_SUFFIX);
	if (fileInfo.exists()) {
		m_bytesCurrentReceived = fileInfo.size();
	}
	QString strUrl = m_url.toString();

	m_request.setUrl(strUrl);
	if (m_bSupportBPoint) {
		QString strRange = QString("bytes=%1-").arg(m_bytesCurrentReceived);
		m_request.setRawHeader("Range", strRange.toLatin1());
	}

	m_reply = m_manager.get(m_request);
	connect(m_reply, &QNetworkReply::downloadProgress, this, &Task::slotUpdateProgress);
	connect(m_reply, &QNetworkReply::readyRead, this, &Task::slotWriteFile);
	connect(m_reply, &QNetworkReply::finished, this, &Task::slotFinish);
	connect(m_reply, SIGNAL(error(QNetworkReply::NetworkError code)), this,SLOT(slotError(QNetworkReply::NetworkError code)));
}

void DownLoad::Task::doUploadWork()
{
		
}

上处代码运行可以看出,QThread 中,每次调用start() 时 ,都会改变线程ID 在这里插入图片描述
因此,QT 的线程开启就是在创建线程,只不过其中含有事件循环机制。另外对于自定义类型,必须指定队列连接。
运行后在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/261614.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

nginx 离线安装 https反向代理

这里写自定义目录标题 安装步骤1.安装nginx所需依赖1.1 安装gcc和gcc-c1.1.1下载依赖包1.1.2 上传依赖包1.1.3安装依赖 1.2 安装pcre1.2.1 下载pcre1.2.2 上传解压安装包1.2.3 编译安装 1.3 下载安装zlib1.3.1 下载zlib1.3.2 上传解压安装包1.3.3 编译安装 1.4 下载安装openssl…

【K8s】3# 使用kuboard管理K8s集群(NFS存储安装)

文章目录 1.NFS是什么2.配置NFS服务器2.1.执行以下命令安装 nfs 服务器所需的软件包2.2.执行命令 vim /etc/exports&#xff0c;创建 exports 文件&#xff0c;文件内容如下2.3.执行以下命令&#xff0c;启动 nfs 服务2.4.检查配置是否生效 3.在客户端测试NFS3.1.执行以下命令安…

基于51的智能交通信号灯设计

文章目录 概要仿真图程序总结 资料下载地址&#xff1a;基于51的智能交通信号灯设计 概要 可实现东西、南北两个方向的红、黄、绿灯按设定的时间亮灭&#xff0c;用以指引交通通行&#xff0c;以倒计时方向显示时间。红、黄、绿交通灯的变化规律为&#xff1a; 1&#xff09…

在国内如何使用某知名GPT,附在线体验链接

在线体验 三种方式&#xff1a; 一、访问openai&#xff0c;之前需要国外手机号&#xff0c;现在好像不用验证了&#xff0c;但是调用API还是需要验证手机号 二、购买一个海外服务器&#xff0c;然后自行在海外服务器上部署一套调用OpenAI接口的服务 三、通过一些国内封装好的网…

Mybatis基本操作

目录 准备工作 删除操作 预编译SQL 增加操作 获取返回的主键 更新操作 准备工作 准备数据库表 emp创建一个新的springboot工程&#xff0c;选择引入对应的起步依赖&#xff08;mybatis、mysql驱动、lombok&#xff09;application.properties中引入数据库连接信息创建对应…

MDC Manifest Configurator

基于华为手册总结摘要的&#xff0c;有分布手册没写的&#xff0c;我给补充上了&#xff0c;比如&#xff1a;定义启动设定 CM配置流程&#xff1a;配置流程涵盖定义数据类型、服务接口、子网信息、部署通信、部署通信协议、定义可执行程序和进程、定义应用进程信息、创建服务…

c语言错误总结

函数 A:void类型函数可以 B&#xff1a;不需要&#xff0c;如果return 不返回任何值&#xff0c;函数会在return语句执行后终止执行&#xff0c;后面的语句不会执行 C&#xff1a;对的 D&#xff1a;不可能&#xff0c;return只能返回一个数据 A:函数不一定有返回值 B:可以…

JS - 设计模式持续学习中

通过例子持续学习JS设计模式中&#xff0c;接下来请跟随我的步伐走进我的学习笔记世界~ 什么是设计模式&#xff1f;我们为什么需要学习设计模式&#xff1f; 设计模式是可以更好解决问题的一种方案。 这意味着什么&#xff1f;如果你开发的项目的功能是固定的&#xff0c;永…

2023年12月GESP认证C++等级考试(八级)真题试卷

2023年12月GESP认证C等级考试&#xff08;八级&#xff09;真题试卷 题目总数&#xff1a;27 总分数&#xff1a;100 选择题 第 1 题 单选题 小杨要从A城到B城&#xff0c;⼜想顺路游览⼀番。他有两个选项&#xff1a;1、坐⾼铁路到C城游览&#xff0c;再坐⾼铁或飞机到B…

微信小程序制作瀑布流

先看效果&#xff1a; 瀑布流分为左侧和右侧 看代码&#xff1a; <view class"shops-tops"><view id"left"><view class"left"><image src"https://pic.imgdb.cn/item/6583d9d6c458853aef979621.jpg" class&quo…

【解决Typora图片不是显示问题】PicGo+Github+Typora+ onedrive/坚果云 实现笔记同步

【解决Typora图片不是显示问题】PicGo、Github、Typora实现笔记同步 写在前面&#xff1a; typora笔记软件使用记录typora图片上传问题&#xff1a;原因分析&#xff1a;解决方案&#xff1a;PicGoGithubTypora 坚果云/onedrive 实现笔记同步第一步. 设置上传模式&#xff1a;u…

配置BGP的基本示例

BGP简介 定义 边界网关协议BGP&#xff08;Border Gateway Protocol&#xff09;是一种实现自治系统AS&#xff08;Autonomous System&#xff09;之间的路由可达&#xff0c;并选择最佳路由的距离矢量路由协议。早期发布的三个版本分别是BGP-1&#xff08;RFC1105&#xff0…

Python的环境搭建环境配置()

Python 环境搭建 一,下载Python 1.去官网 www.python.org 下载环境 2.如图点击Download 3.选择Windows 4.如图直接下载 5.直接勾选 6.后面就一直默认选项 Win11 安装目录 不能放在C盘的ProgramFIle路径下 二,测试环境是否安装成功 1.winR 输入cmd 2.输入python --versio…

【K8s】2# 使用kuboard管理K8s集群(kuboard安装)

文章目录 安装 Kuboard v3部署计划 安装登录测试 安装 Kuboard v3 部署计划 在正式安装 kuboard v3 之前&#xff0c;需做好一个简单的部署计划的设计&#xff0c;在本例中&#xff0c;各组件之间的连接方式&#xff0c;如下图所示&#xff1a; 假设用户通过 http://外网IP:80…

AIGC:阿里开源大模型通义千问部署与实战

1 引言 通义千问-7B&#xff08;Qwen-7B&#xff09;是阿里云研发的通义千问大模型系列的70亿参数规模的模型。Qwen-7B是基于Transformer的大语言模型, 在超大规模的预训练数据上进行训练得到。预训练数据类型多样&#xff0c;覆盖广泛&#xff0c;包括大量网络文本、专业书籍…

[CVPR-23] Instant Volumetric Head Avatars

[paper | code | proj] 本文提出INSTA。INSTA是一种backward mapping方法。该方法基于NeRF建立标准空间&#xff0c;形变空间&#xff08;任意表情&#xff09;通过映射回标准空间&#xff0c;实现渲染。为实现形变空间中任意点向标准空间的映射&#xff0c;对形变空间中的任意…

rk3568 bootLoader编译

Linux系统uboot、linux kernel、rootfs移植学习笔记&#xff08;一&#xff09;_uboot 删除环境变量-CSDN博客 板信息配置文件&#xff1a;device/rockchip/rk356x/BoardConfig-IAC-RK3568-MB-BETA-V1_00.mk uboot编译入口 Linux系统uboot、linux kernel、rootfs移植学习笔记&…

许久不见钱伯斯,他说大部分AI公司会失败,但值得一拼

前言&#xff1a;AI大饼是否还不够大&#xff0c;于是我也想多画一画 【科技明说 &#xff5c; 科技热点关注】 许多年不见钱伯斯&#xff0c;他发声指出大部分AI公司会失败&#xff0c;但值得一拼。 钱伯斯(John Chambers)以长期担任思科(Cisco)首席执行官的硅谷传奇人物而…

C++入门【12-C++ 数组】

C 数组 C 支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往被认为是一系列相同类型的变量。 数组的声明并不是声明一个个单独的变量&#xff0c;比如 number0、number1、...、number99&#xff0…

软协打卡---内网穿透实现

注意&#xff1a;仅为个人学习知识&#xff0c;其中理论知识不一定正确。 目录 1.前言 2.ip与域名的简单说明 ip与域名 公共ip和内网ip 内网穿透了解 为什么使用内网穿透 内网穿透是什么 3.PHPSTUDY了解 4.花生壳的使用 最终结果&#xff1a; &#xff08;实际上大部…