Qt提供了QThread类表示线程
- 创建线程的步骤:
- 创建QThread类的实例
- 指定线程的入口函数
对于Linux的线程函数,是在参数部分使用函数指针指定线程的入口函数
对于Qt,则先创建QThread子类,再重写其中的run函数,起到指定入口函数的方式(这里多态)同事件处理
start,调用系统API创建线程,新线程创建出来后会自动执行run函数
finish,线程结束时会发出信号,可以通过该信号来实现线程的清理工作
在Qt中,为了防止多线程修改界面的不安全现象,Qt选择一刀切,对于界面的控件状态修改必须在主线程中进行
分为三个部分,公有函数、公有槽函数,信号,Qt提供的静态成员函数
父对象和父类是两回事,父类是继承关系,父对象是从属关系
// 构造函数
QThread::QThread(QObject *parent = Q_NULLPTR);
指定一个父对象就可以了
// 判断线程中的任务是不是处理完毕了
bool QThread::isFinished() const;
// 判断子线程是不是在执行任务
bool QThread::isRunning() const;
// Qt中的线程可以设置优先级
// 得到当前线程的优先级
Priority QThread::priority() const;
void QThread::setPriority(Priority priority);
优先级:
QThread::IdlePriority --> 最低的优先级
QThread::LowestPriority
QThread::LowPriority
QThread::NormalPriority
QThread::HighPriority
QThread::HighestPriority
QThread::TimeCriticalPriority --> 最高的优先级
QThread::InheritPriority --> 子线程和其父线程的优先级相同, 默认是这个
// 退出线程, 停止底层的事件循环
// 退出线程的工作函数
void QThread::exit(int returnCode = 0);
// 调用线程退出函数之后, 线程不会马上退出因为当前任务有可能还没有完成, 调回用这个函数是
// 调用wait后会等待任务执行, 执行完退出线程, 一般情况下会在 exit() 后边调用这个函数
bool QThread::wait(unsigned long time = ULONG_MAX);
// 和调用 exit() 效果是一样的
// 调用这个函数之后, 再调用 wait() 函数
[slot] void QThread::quit();
// 启动子线程
[slot] void QThread::start(Priority priority = InheritPriority);
// 线程退出, 可能是会马上终止线程, 一般情况下不使用这个函数
[slot] void QThread::terminate();
// 线程中完成后, 会发出该信号
[signal] void QThread::finished();
// 发出该信号后表示线程开始工作,启动线程后没必要捕捉该信号
[signal] void QThread::started();
主线程和子线程间通过信号和槽进行数据传递
// 返回一个指向管理当前执行线程的QThread的指针
[static] QThread *QThread::currentThread();
// 返回可以在系统上运行的理想线程数 == 和当前电脑的 CPU 核心数相同
[static] int QThread::idealThreadCount();
// 线程休眠函数
[static] void QThread::msleep(unsigned long msecs); // 单位: 毫秒
[static] void QThread::sleep(unsigned long secs); // 单位: 秒
[static] void QThread::usleep(unsigned long usecs); // 单位: 微秒
// 子线程要处理什么任务, 需要写到 run() 中
[virtual protected] void QThread::run();
该函数没有参数和返回值,是一个受保护的虚函数,如果让子线程处理某些任务,需要将任务写到run内部,调用start函数就能执行线程内部的run函数
受保护说明该函数不能在类外使用,需要通过调用start槽函数使run函数运行执行任务
------------------------------------------------
Widget.h
#include
#include "thread.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
Thread thread;
void handle();
};
------------------------------------------------------------
Widget,cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//在构造函数中启动线程
thread.start();
//连接信号槽,通过槽函数更新界面
connect(&thread,&Thread::notify,this,&Widget::handle);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handle()
{
//修改界面内容
int val=ui->lcdNumber->intValue();
val--;
ui->lcdNumber->display(val);
}
--------------------------------------
Thread.h 继承自QThread
#include
#include
class Thread : public QThread
{
Q_OBJECT
public:
Thread();
//最重要的目的是重写run函数
void run();
signals:
void notify();
};
-------------------------------------------
#include "thread.h"
Thread::Thread()
{
}
void Thread::run()
{
//在run函数中不能修改界面的内容
//可以在run中计时,每过一秒通知主线程去修改界面计时器的值
for(int i=0;i<10;i++)
{
sleep(1);
//休眠完后,发送信号通知主线程
emit notify();
}
}
方式1:
- 创建线程类,该类继承QThread类
- 在线程的子类中重写run函数
- 在主线程类中new一个子线程的类对象,让该子线程对象调用start函数
子类头文件
#include
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
protected:
void run();
signals:
// 自定义信号, 传递数据
void curNumber(int num);
public slots:
};
子类源文件
#include "mythread.h"
#include
MyThread::MyThread(QObject *parent) : QThread(parent)
{}
void MyThread::run()
{
qDebug() << "当前线程对象的地址: " << QThread::currentThread();
int num = 0;
while(1)
{
emit curNumber(num++);
if(num == 10000000)
{
break;
}
QThread::usleep(1);
}
qDebug() << "run() 执行完毕, 子线程退出...";
}
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "mythread.h"
#include
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
qDebug() << "主线程对象地址: " << QThread::currentThread();
// 创建子线程
MyThread* subThread = new MyThread;
connect(subThread, &MyThread::curNumber, this, [=](int num)
{
ui->label->setNum(num);
});
connect(ui->startBtn, &QPushButton::clicked, this, [=]()
{
// 启动子线程
subThread->start();
});
}
MainWindow::~MainWindow()
{
delete ui;
}