Qt常见的创建线程的方式有两种:
优缺点:
方法一:
1. 线程中的对象必须在run函数中创建,线程的范围只在run函数中。原因:QThread以及它继承的类都是属于主线程的,tun函数是QThread开辟的线程,所以想要创建属于新的线程的实例就必须在run函数中进行初始化,实例化。
3. 线程无法接收信号,只能发送信号。
方法二:
1. 比较灵活简洁
2. 在线程中调用其他函数,函数还是属于线程的。
第一种比较好理解和上手:
#ifdef _MSC_VER
#if _MSC_VER >=1600 // VS2010版本号是1600
#pragma execution_character_set("utf-8")
#endif
#endif
#include "timethread.h"
TimeThread::TimeThread()
{
}
void TimeThread::run()
{
// int num = 10;
// timer = new QTimer(this);
// connect(timer, SIGNAL(timeout()), this, SLOT(timeStop()));
// timer->start(1000 * num);
// qDebug() << "这个是线程";
}
void TimeThread::timeStop()
{
// QMessageBox msgBox;
// msgBox.setText("The document has been modified.");
// msgBox.exec();
// timer->stop();
}
.h文件:
#ifndef TIMETHREAD_H
#define TIMETHREAD_H
#include <QThread>
#include <QDebug>
#include <QTimer>
#include <QMessageBox>
class TimeThread: public QThread
{
Q_OBJECT
public:
TimeThread();
// explicit TimeThread();
private slots:
void timeStop();
protected:
void run();
//private:
// QTimer *timer;
};
#endif // TIMETHREAD_H
另一种是
#if _MSC_VER >=1600 //VS2010版本号是1600
#pragma execution_character_set("utf-8")
#endif
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow) {
ui->setupUi(this);
auto *worker = new Worker ;
// 调用 moveToThread 将该任务交给 workThread
worker->moveToThread(&workerThread);
// operate 信号发射后启动线程工作
connect(this, SIGNAL(operate(const int)), worker, SLOT(doWork(int)));
// 该线程结束时销毁
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
// 线程结束后发送信号,对结果进行处理
connect(worker, SIGNAL(resultReady(int)), this, SLOT(handleResults(int)));
// 启动线程
workerThread.start();
// 发射信号,开始执行
qDebug() << "主线程发出线程执行的信号" ;
qDebug() << "\t线程的ID:" << QThread::currentThreadId() << '\n' ;
emit operate(0);
for (int i = 0; i != 500; ++i) {
qDebug() << "主线程的循环" << i;
}
}
void MainWindow::handleResults(const int result) {
qDebug() << "接受线程结束的信号" ;
qDebug() << "\tCurrent thread ID: " << QThread::currentThreadId() << '\n' ;
qDebug() << "\tThe last result is: " << result ;
}
MainWindow::~MainWindow() {
qDebug() << "主线程的析构函数";
workerThread.quit();
workerThread.wait();
delete ui;
}
Worker::Worker(QObject *parent) {
qDebug() << "线程的构造函数";
}
Worker::~Worker() {
qDebug() << "~Worker()" << "thread:" << QThread::currentThreadId();
}
void Worker::doWork(int parameter) {
qDebug() << "接收到线程开始的信号,这个信号是主程序发出的" ;
qDebug() << "\t线程的ID " << QThread::currentThreadId();
// 循环一百万次
for (int i = 0; i != 500; ++i) {
qDebug() << "线程中的循环" << parameter;
++parameter ;
}
// 发送结束信号
qDebug() << "\t线程运行结束\n" ;
emit resultReady(parameter);
}
.h文件:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
// 创建一个线程类,用来进行比较耗时的工作
#include <QObject>
#include <QDebug>
#include <QThread>
class Worker : public QObject {
Q_OBJECT
public:
explicit Worker(QObject *parent = nullptr);
~Worker();
public slots:
// doWork 定义了线程要执行的操作
void doWork(int parameter);
signals:
// 线程完成工作时发送的信号
void resultReady(const int result);
};
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
// 处理子线程执行的结果
static void handleResults(int result);
signals:
// 发送信号,触发线程
void operate(const int);
private:
Ui::MainWindow *ui;
QThread workerThread;
};
#endif // MAINWINDOW_H
附录:
Qt connect的第5个参数:
1、Qt::AutoConnection: 默认值,使用这个值则连接类型会在信号发送时决定。如果接收者和发送者在同一个线程,则自动使用Qt::DirectConnection类型。如果接收者和发送者不在一个线程,则自动使用Qt::QueuedConnection类型。
2、Qt::DirectConnection:槽函数会在信号发送的时候直接被调用,槽函数运行于信号发送者所在线程。效果看上去就像是直接在信号发送位置调用了槽函数。这个在多线程环境下比较危险,可能会造成奔溃。
3、Qt::QueuedConnection:槽函数在控制回到接收者所在线程的事件循环时被调用,槽函数运行于信号接收者所在线程。发送信号之后,槽函数不会立刻被调用,等到接收者的当前函数执行完,进入事件循环之后,槽函数才会被调用。多线程环境下一般用这个。
4、Qt::BlockingQueuedConnection:槽函数的调用时机与Qt::QueuedConnection一致,不过发送完信号后发送者所在线程会阻塞,直到槽函数运行完。接收者和发送者绝对不能在一个线程,否则程序会死锁。在多线程间需要同步的场合可能需要这个。
5、Qt::UniqueConnection:这个flag可以通过按位或(|)与以上四个结合在一起使用。当这个flag设置时,当某个信号和槽已经连接时,再进行重复的连接就会失败。也就是避免了重复连接。