一个应用程序一般都只有一个线程,但是如果某个比较消耗时间的计算或操作,例如在网络通信中的文件传输,在一个线程中操作,用户的界面就会出现冻结不能及时响应的情况。这种情况下,就需要多线程,Qt为多线程操作提供了完整的支持。
QThread是线程类,是实现多线程操作的核心类,一般是从Qhread继承定义自己的线程类。同时线程之间的同步是其交互的主要问题,Qt提供了QMutex、QMutexLocker、QReadWriteLock、QWaitCodition、QSemaphore等多种类用于实现线程之间的同步。同时Qt还提供了Qt Concurrent模块,提供一些高级的API实现多线程编程而无需使用QMutex、QWaitCodition、QSemaphore等基础操作。使用Qt Concurrent实现多线程程序可以自动根据处理器内核个数调整线程个数。
Qt Concurrent模块讲解在该文章中不涉及,本文主要介绍QThread实现多线程的方法,关于QMutex、QWaitCodition、QSemaphore等实现线程同步的方法将在以后的文章中整理。
以下是QT官方提供的QThread Class类的主要接口函数。
The QThread class provides a platform-independent way to manage threads. More...
QThread类提供了不依赖于平台的管理线程的方法。
Header: | #include <QThread> |
qmake: | QT += core |
Inherits: |
Public Types
enum | Priority { IdlePriority, LowestPriority, LowPriority, NormalPriority, ..., InheritPriority } |
QThread(QObject *parent = nullptr) | |
virtual | ~QThread() |
QAbstractEventDispatcher * | eventDispatcher() const |
void | exit(int returnCode = 0) |
bool | isFinished() const |
bool | isInterruptionRequested() const |
bool | isRunning() const |
int | loopLevel() const |
QThread::Priority | priority() const |
void | |
void | setEventDispatcher(QAbstractEventDispatcher *eventDispatcher) |
void | setPriority(QThread::Priority priority) |
void | setStackSize(uint stackSize) |
uint | stackSize() const |
bool | wait(unsigned long time = ULONG_MAX) |
Reimplemented Public Functions
virtual bool | event(QEvent *event) override |
void | quit() |
void | start(QThread::Priority priority = InheritPriority) |
void |
void | finished() |
void | started() |
QThread * | create(Function &&f, Args &&... args) |
QThread * | create(Function &&f) |
QThread * | |
Qt::HANDLE | |
int | |
void | msleep(unsigned long msecs) |
void | sleep(unsigned long secs) |
void | usleep(unsigned long usecs) |
void |
int | exec() |
virtual void | run() |
void | setTerminationEnabled(bool enabled = true) |
一个QThread类的对象管理一个线程,一般从QThread继承一个自定义类,并重定义虚函数run(),在run()函数里实现线程需要完成的任务。QThread是QObject的子类,所以可以使用信号与槽机制。
下面给出投掷骰子的例子:
##pro文件:
#-------------------------------------------------
#
# Project created by QtCreator 2023-01-30T21:02:13
#
#-------------------------------------------------
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = QThread-Demo
TEMPLATE = app
# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS
# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CONFIG += c++11
SOURCES += \
main.cpp \
widget.cpp \
qdicethread.cpp
HEADERS += \
widget.h \
qdicethread.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
//子线程头文件qdicethread.h
#ifndef QDICETHREAD_H
#define QDICETHREAD_H
#include <QObject>
#include <QThread>
class QDiceThread : public QThread
{
Q_OBJECT
public:
explicit QDiceThread();
virtual void run() override;
void diceBegin();
void dicePause();
void stopThread();
private:
int m_seq = 0; //投掷骰子次数
int m_diceValue; //骰子点数
bool m_Paused = true; //暂停
bool m_stop = false; //停止
signals:
void newValue(int seq, int diceValue);
public slots:
};
#endif // QDICETHREAD_H
//子线程qdicethread.cpp
#include "qdicethread.h"
#include <QDebug>
#include <QTime>
QDiceThread::QDiceThread()
{
}
void QDiceThread::run()
{
qWarning() << "子线程的ID = " << currentThreadId();
m_stop = false;
m_seq = 0;
qsrand(QTime::currentTime().msec());//随机初始化,qsrand是线程安全的
while (!m_stop)
{
if (!m_Paused) {
m_diceValue = qrand();//获取随机数
m_diceValue = (m_diceValue % 6) + 1;
m_seq++;
emit newValue(m_seq, m_diceValue);
}
msleep(500);//500ms
}
quit();//相当于exit(0),退出线程事件循环
}
void QDiceThread::diceBegin()
{
m_Paused = false;
}
void QDiceThread::dicePause()
{
m_Paused = true;
}
void QDiceThread::stopThread()
{
m_stop = true;
}
//头文件widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "qdicethread.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void on_clear_clicked();
void on_started_clicked();
void on_action_clicked();
void on_pause_clicked();
void on_stop_clicked();
//自定义槽函数
void onthread_started();
void onthread_finished();
void onthread_newValue(int seq, int diceValue);
protected:
void closeEvent(QCloseEvent *event);
private:
Ui::Widget *ui;
QDiceThread m_thread;
};
#endif // WIDGET_H
//头文件widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
connect(&m_thread, &QDiceThread::started, this, &Widget::onthread_started);
connect(&m_thread, &QDiceThread::finished, this, &Widget::onthread_finished);
connect(&m_thread, &QDiceThread::newValue, this, &Widget::onthread_newValue);
qWarning() << "主线程的ID = " << QThread::currentThreadId();
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_started_clicked()
{
m_thread.start();//线程启动
ui->started->setEnabled(false);
ui->stop->setEnabled(true);
ui->action->setEnabled(true);
ui->pause->setEnabled(false);
}
void Widget::on_stop_clicked()
{
m_thread.stopThread();//线程结束
ui->action->setEnabled(false);
ui->stop->setEnabled(false);
ui->action->setEnabled(false);
ui->started->setEnabled(true);
}
void Widget::on_action_clicked()
{
//开始投掷骰子
m_thread.diceBegin();
ui->action->setEnabled(false);
ui->pause->setEnabled(true);
}
void Widget::on_pause_clicked()
{
//暂停投掷
m_thread.dicePause();
ui->pause->setEnabled(false);
ui->action->setEnabled(true);
}
void Widget::onthread_started()
{
qWarning() << "Thread的状态:thread started";
ui->label->setText("Thread的状态:thread started");
}
void Widget::onthread_finished()
{
qWarning() << "Thread的状态:thread finished";
ui->label->setText("Thread的状态:thread finished");
}
void Widget::onthread_newValue(int seq, int diceValue)
{
QString str = QString::asprintf("第%d次掷骰子, 点数为 %d", seq, diceValue);
ui->plainTextEdit->appendPlainText(str);
}
void Widget::closeEvent(QCloseEvent *event)
{
窗口关闭事件,必须结束线程
if (m_thread.isRunning()) {
m_thread.stopThread();
m_thread.wait();
}
event->accept();
}
void Widget::on_clear_clicked()
{
ui->plainTextEdit->clear();
}
//main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
程序实现效果: