当前位置:   article > 正文

Qt线程基础使用指南_qt线程使用

qt线程使用

Qt的线程一共3种使用方式:继承QThread、继承QRunnable、调用moveToThread()方法。本文旨在系统的记录这3种方法的使用过程,以及解决使用这些方法中遇到的bug。

一、继承QThread

1、创建线程文件

继承基类QThread即可
QThread

2、启动线程

继承自QThread的线程类,可以在主线程中调用start()方法启动该子线程。子线程一般都有限定的生存周期,所以我们在创建线程时应该设置好线程的关闭条件。

从qt5以后的线程,都提供了一个requestInterruption()的方法用于请求结束线程,对应的有isInterruptionRequested()方法用于控制是否结束线程的运行。

所以,我们需要把isInterruptionRequested()设置为线程循环执行的条件。

//在线程文件中
void MyQThread::run()
{
	int num = 0;
	while (!isInterruptionRequested())
	{
		qDebug() << QThread::currentThreadId << " say : " << num++;
		QThread::sleep(1);
	}
	qDebug() << "work done";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

在A线程中使用start()方法启动线程,需要注意的是,如果A线程是整个程序,那么在创建子线程变量时应该将该变量设置为类变量。

在qt中,方法中的变量申请的内存只能在该方法结束前释放,如果该方法运行结束,那么系统会回收该变量的地址,但不会帮变量释放内存,那时就相当于该内存的地址丢失了,只有等到程序结束才会被释放。

myQthread = new MyQThread(this); 
myQthread->start();
  • 1
  • 2

参数 this 解释
A线程创建子线程时,this指代的就是A线程,this可以被省略。
如果使用了this,此时子线程的生命周期和A线程一样长,A线程结束时子线程也会被结束(当然我们可以提前释放该子线程);
如果省略了this,此时子线程的生命周期将比A线程还长,只有当我们手动delete它或者整个程序结束,该子线程才会被结束。

3、线程通信

由于QThread是继承于QObject的,所以继承QThread类创建的线程可以直接将线程中执行的结果通过信号的方式发到主程序。

//在A线程中启动子线程
void QThreadTest::startThread1()
{
    qDebug() << "ready to start thread1!!!";
    myQthread = new MyQThread();
    myQthread->start();
    connect(myQthread, SIGNAL(toResetNum()), this, SLOT(resetNum())); //通过槽函数通信
}

//在子线程中每3个数发送一次信号
void MyQThread::run()
{
	int num = 0;
	while (!isInterruptionRequested())
	{
		qDebug() << QThread::currentThreadId << " say : " << num++;
		QThread::sleep(1);
		if (num % 3 == 0) emit toResetNum();
	}
	qDebug() << "work done";
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

4、结束线程

调用qt5之后提供的**requestInterruption()**方法主动结束线程,我们希望子线程的内存在子线程运行结束就被释放,那么我们需要显式的调用delete()方法删除该线程占用的内存。

quit()方法用于结束通过调用exec()方法循环执行的线程,使用这种方法退出的线程,在回收内存之前也需要判断线程是否还在运行。

//在A线程中关闭子线程
void QThreadTest::quitThread1()
{
    qDebug() << "ready to quit thread1...";

    myQthread->requestInterruption(); //发送结束子线程的信号
    while (myQthread->isRunning()); //等待子线程运行结束
    delete(myQthread); //销毁子线程,回收内存
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

二、继承QRunnable

1、创建线程文件

继承基类QRunnable
QRunnable
文件生成后,如果报错

E0020 未定义标识符 “QObject”
E0493 没有与指定类型匹配的 重载函数 “QtClass::QtClass” 实例

那么添加头文件,并删除构造函数的形参列表。

#pragma once

#include <QRunnable>
#include <QThread>

class MyRThread  : public QObject,public QRunnable
{
	Q_OBJECT

public:
	MyRThread();
	~MyRThread();
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2、启动线程

与QThread不同的是,QThread线程可以直接调用start()函数启动,而QRunnable线程需要借助QThreadPool进行启动。

//在A线程中启动子线程
void QThreadTest::startThread2()
{
    qDebug() << "ready to start thread2!!!";
    myRthread = new MyRThread();
    
    thread_pool->start(myRthread); //非全局线程池
    qDebug()<<"thread count in threadpool: "<< thread_pool->activeThreadCount(); //1
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

由于**QRunnable中没有requestInterruption()**方法,所以我们只有通过手动设置停止条件控制线程运行。

//在线程文件中
void MyRThread::run()
{
	int num = 0;
	while (!ifStop)
	{
		qDebug() << QThread::currentThreadId << " say : " << num++;
		QThread::sleep(1);
	}
	ifStop = false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

3、线程通信

因为QRunnable没有继承于QObject,所以没法使用信号槽与外界通信,一般使用QMetaObject::invokeMethod()。

void MyRThread::run()
{
	int num = 0;
	while (!ifStop)
	{
		qDebug() << QThread::currentThreadId << " say : " << num++;
		QThread::sleep(1);
		if(num%3==0)
			QMetaObject::invokeMethod(myObject, "resetNum"); //每隔3个数调用一次A线程的方法
	}
	ifStop = false;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

4、结束线程

继承QRunnable的线程在运行结束后可以自动释放资源,不需要调用delete()方法。

//在A线程中关闭子线程
void QThreadTest::quitThread2()
{
    qDebug() << "ready to quit thread2...";
    myRthread->setStop(); //线程结束后自动释放资源
    QThread::sleep(1);
    qDebug() << "thread count in threadpool: " << thread_pool->activeThreadCount(); //0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

三、使用moveToThread方法

未完待续

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/104561
推荐阅读