Qt 简单易懂的使用线程

简介

线程就是运行程序的东西,比如电脑系统就需要一个线程去运行,但是如果想打开软件,就必须给他开一个线程让其运行,否则一个线程是无法运行两个程序的,就会出现卡死状态。

qt使用线程必须自定义类并继承使用。

qt线程有两种使用方式:

  1. 第一种:qt4.8版本之前所使用的,继承于QThread类,重写run方法进行实现;
  2. 第二种:qt4.8版本之后,一直大力推崇的线程使用方式,继承于QOBject类,使用moveToThread方式实现。

这篇博客介绍(记录)第二种方式是如何使用线程的。

线程

线程的使用方式前篇一律,这里写下项目中常用的方式。

当然线程的使用离不开信号与槽的机制,我们这里使用信号与槽可以得出一个三角恋的关系,如下图:
在这里插入图片描述

由此图可以得出三角关系可知:

  1. 主程序与线程进行信号与槽的绑定;
  2. 线程去执行方法;
  3. 方法类与主程序进行信号与槽的绑定;
  4. 线程也与主程序进行信号与槽的绑定。

1. 自定义类继承QObject

自定义的线程类中写下头文件#include <QObject>

当然线程的cpp文件需要包含执行方法的类的头文件。

继承如下:

class MyThread : public QObject {
	Q_OBJECT
};

类中包含一个信号与一个槽函数:

public:
	// 槽函数,此函数调用方法执行(参数必须和关联的信号的参数一致)
	void triggerFunc(Test *test);
	
signals:
	// 信号,函数执行完毕后发射信号告诉主程序执行完毕了
	void trigger();

2. 创建一个新类用于执行需要的操作

这里我们创建一个Test类用于操作,包含一个test()方法用于执行真正的操作。

class Test : public QWidget {
public:
	// 真正需要执行的操作
	void test();
};

3. 创建主程序类

主程序需要包含头文件:

  1. 自定义线程类;
  2. 需要执行操作的类;
  3. 系统线程类。#include <QThread>
class TestQThreadThree : public QWidget {
	Q_OBJECT

signals:
	// 发射信号触发线程执行,信号带参,将需要的参数传给线程
	void trr(Test *test);
};

主程序所需要做的操作:

  1. 发射信号触发线程;
  2. 接收信号进行页面标签文本更改;
  3. 接收线程执行完毕的信号,qDebug()打印“线程执行完毕”字样

在构造函数中:
构造函数中,需要定义一个系统线程对象指定父类为this,再定义自定义线程类不能指定父对象。

最后自定义线程对象调用moveToThread()函数指定系统线程对象为父类即可。
例:

myThread = new MyThread;
thread = new QThread(this);
// 自定义线程指定旧线程为父类
myThread->moveToThread(thread);

最后在发射信号前使用系统线程thread->start();就可以执行线程啦。

发射的信号可以不带参数,但是建议带参,这样可以更好的理解信号对应需要绑定哪个槽函数。


代码

下面是例子全部代码
(代码有详细注释)

线程类

MyThread.h

#pragma once

#include <QObject>

//#include "Test.h"

class Test;

class MyThread : public QObject
{
	Q_OBJECT

public:
	MyThread();
	~MyThread();

	// 主程序触发线程执行的槽方法(参数必须和关联的信号的参数一致)
	void triggerFunc(Test *test);

signals:
	void trigger();


private:
	//Test *test;
};

MyThread.cpp

#include "MyThread.h"
#include "Test.h"


MyThread::MyThread() {
	//test = new Test;
}

MyThread::~MyThread() {
	/*if (test)
	delete test;*/
}



void MyThread::triggerFunc(Test *test) {
	// 根据传过来的参数,调用方法执行
	test->test();	

	// 执行完发射信号,线程已经执行完毕
	emit trigger();
}

需要执行操作的类

Test.h

#pragma once

#include <QWidget>

class Test : public QWidget
{
	Q_OBJECT

public:
	Test();
	~Test();

	// 真正需要执行的操作
	void test();
	// 控制方法的运行停止
	void setFlags(bool b);


signals:
	// 给主程序发射的信号
	void uis();
	
private:
	// 控制变量
	bool flags;
};

Test.cpp

#include "Test.h"

#include <Windows.h>
#include <QDebug>

Test::Test() {	
	flags = false;
}

Test::~Test() {
}


void Test::test() {
	static int i = 0;


	while (!flags) {
		Sleep(200);	// 休眠200ms

		emit uis();	// 发射信号,主程序响应同时做一些操作

		qDebug() << i++;

		if (flags == true) {
			break;
		}
	}

}


void Test::setFlags(bool b) {
	flags = b;
}

主程序类

TestQThreadThree.h

#pragma once

#include <QtWidgets/QWidget>
#include "ui_TestQThreadThree.h"

#include "MyThread.h"
#include "Test.h"
#include <QThread>

class TestQThreadThree : public QWidget {
	Q_OBJECT

public:
	TestQThreadThree(QWidget *parent = Q_NULLPTR);
	~TestQThreadThree();

	// 设置Lable标签文本
	void setLabel();

signals:
	// 信号带参,将需要的参数传给线程
	void trr(Test *test);

private slots:
	void on_startBtn_clicked();	// 开始按钮槽方法
	void on_pauseBtn_clicked();	// 停止按钮槽方法
	


private:
	Ui::TestQThreadThreeClass ui;
	// 自定义线程
	MyThread *myThread;
	// 系统线程
	QThread *thread;
	// 所需要执行操作的方法类
	Test *test;
};

TestQThreadThree.cpp

#include "TestQThreadThree.h"	
#include <QDebug>

#pragma execution_character_set("utf-8") // qt支持显示中文

TestQThreadThree::TestQThreadThree(QWidget *parent)
	: QWidget(parent) {
	ui.setupUi(this);

	test = new Test;

	myThread = new MyThread;	// 不能指定父类
	thread = new QThread(this);
	// 自定义线程指定旧线程为父类
	myThread->moveToThread(thread);

	// 主程序发射信号,线程响应执行槽函数
	connect(this, &TestQThreadThree::trr, myThread, &MyThread::triggerFunc);
	// test对象发射信号,主程序响应执行槽函数
	connect(test, &Test::uis, this, &TestQThreadThree::setLabel);
	// 线程执行完毕发射信号,主线程响应,知道线程已经执行完毕
	connect(myThread, &MyThread::trigger, 
	[=]() {
		QString str = "线程执行完毕!!!";
		//ui.numberLabel->setText(str);

		qDebug() << str;
	});


	// 当点击右上角叉时,关闭线程
	//connect(this, &TestQThreadThree::destroyed,
	//[=]() {
	//	if (thread->isRunning()) {
	//		// 停止线程
	//		test->setFlags(true);

	//		/* - 推荐使用 - */
	//		// 停止线程
	//		thread->quit();
	//		// 等待线程处理完当前工作
	//		thread->wait();

	//		// 强制关闭线程,线程没有执行完也进行强制关闭,不推荐使用
	//		// thread->terminate();
	//	}

	//	// 释放线程
	//	delete thread;
	//	delete myThread;

	//	delete test;
	//});
}


TestQThreadThree::~TestQThreadThree() {
/*注意需要停止线程后才进行线程资源的释放*/
	if (thread->isRunning()) {
		// 停止线程
		test->setFlags(true);

		/* - 推荐使用 - */
		// 停止线程
		thread->quit();
		// 等待线程处理完当前工作
		thread->wait();

		// 强制关闭线程,线程没有执行完也进行强制关闭,不推荐使用
		// thread->terminate();
	}

	// 释放线程
	delete thread;
	delete myThread;

	delete test;
}


// isRunning():线程如果启动,返回true,否则返回false
void TestQThreadThree::on_startBtn_clicked() {
	// 判断线程是否在运行
	if (thread->isRunning() == true) {
		return;
	}

	// 启动线程,但是没有启动线程处理函数
	thread->start();
	test->setFlags(false);
	// 主程序发射信号,线程才进行真正的操作
	emit trr(test);	
}


void TestQThreadThree::on_pauseBtn_clicked() {
	if (thread->isRunning() == false) {
		return;
	}

	test->setFlags(true);
	// 停止线程
	thread->quit();
	// 等待线程处理完当前工作
	thread->wait();
}


void TestQThreadThree::setLabel() {
	static int i = 0;
	ui.numberLabel->setText(QString("%1").arg(++i));
}

运行截图:
点击开始后:
在这里插入图片描述
点击暂停后:
在这里插入图片描述

注意事项:

  1. 线程关闭有两种方式:第一种是强制关闭thread->terminate();;强制关闭线程,线程没有执行完也进行强制关闭,不推荐使用。第二种是等待线程执行完毕后才进行关闭,thread->quit(); thread->wait();,推荐使用。
  2. 检查线程是否在运行状态可以使用thread->isRunning();进行判断,线程如果启动,返回true,否则返回false。

总结:
感觉这篇博客写失败了,没有详细的写清楚线程是怎么回事,理解力强的小伙伴看我画的那幅三角恋图,在结合代码进行理解即可。
简单来讲线程就是执行数据处理,主程序触发信号给线程去调用函数执行。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

cpp_learners

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值