Qt多线程编程学习
1. 项目概述
本项目展示了Qt中多线程编程的基本用法,通过继承QThread
类创建自定义线程,并演示了线程的启动、执行和销毁过程。项目包含一个简单的用户界面,用户可以通过按钮控制线程的启动和结束。
1.1 项目结构
项目包含以下文件:
- 51.pro:项目配置文件
- main.cpp:程序入口
- widget.h:主窗口类定义,包含自定义线程类定义
- widget.cpp:主窗口类实现
- widget.ui:主窗口界面设计文件
1.2 功能特点
- 继承
QThread
类创建自定义线程 - 重写
run()
方法定义线程执行逻辑 - 使用
deleteLater()
实现线程的自动销毁 - 通过按钮控制线程的启动和结束
- 使用
qDebug()
输出线程状态信息
2. 代码实现
2.2 主函数(main.cpp)
#include "widget.h"#include <QApplication>int main(int argc, char *argv[])
{QApplication a(argc, argv); // 创建应用程序对象Widget w; // 创建主窗口对象w.show(); // 显示主窗口return a.exec(); // 进入应用程序事件循环
}
2.3 Widget类定义(widget.h)
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QThread>
#include <QDebug>class MyThread; // 前向声明QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE// 主窗口类
class Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr); // 构造函数~Widget(); // 析构函数private slots:void on_pushButton_clicked(); // 开启线程按钮槽函数void on_pushButton_2_clicked(); // 结束线程按钮槽函数private:Ui::Widget *ui; // UI对象指针MyThread *myThread; // 线程对象指针
};// 自定义线程类
class MyThread : public QThread
{Q_OBJECTpublic:// 构造函数MyThread(QWidget *parent = nullptr) {Q_UNUSED(parent) // 标记参数未使用,避免编译器警告}// 析构函数~MyThread() {qDebug() << "线程销毁" << endl;}// 重写run()方法,定义线程执行逻辑// 注意:只有这个run()方法在新的线程里面执行void run() override {qDebug() << "线程开启" << endl;sleep(5); // 模拟耗时操作,睡眠5秒qDebug() << "线程结束" << endl;deleteLater(); // 线程执行完毕后自动销毁对象}
};#endif // WIDGET_H
2.4 Widget类实现(widget.cpp)
#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this); // 设置UI// 一个应用程序至少要一个进程,QProcess// 一个进程至少会有一个线程,QThread// 注释掉的代码:在构造函数中创建线程对象// myThread = new MyThread(this);
}Widget::~Widget()
{delete ui; // 释放UI资源
}// 开启线程按钮点击事件处理函数
void Widget::on_pushButton_clicked()
{// system("sleep 5"); // 系统调用,会阻塞主线程// system("ping 192.168.1.1"); // 系统调用,会阻塞主线程// 启动线程的两种方式:// 方式1:使用成员变量(注释掉的代码)// myThread->start();// 方式2:动态创建线程对象(推荐)MyThread *testThread = new MyThread(this);testThread->start(); // 启动线程,会调用run()方法
}// 结束线程按钮点击事件处理函数
void Widget::on_pushButton_2_clicked()
{// 终止线程(注释掉的代码)// 注意:terminate()方法强制终止线程,可能导致资源泄漏// 不推荐使用,应该让线程自然结束// if (!myThread->isFinished())// myThread->terminate();
}
2.5 主窗口界面(widget.ui)
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0"><class>Widget</class><widget class="QWidget" name="Widget"><property name="geometry"><rect><x>0</x><y>0</y><width>800</width><height>480</height></rect></property><property name="windowTitle"><string>Widget</string></property><!-- 开启线程按钮 --><widget class="QPushButton" name="pushButton"><property name="geometry"><rect><x>70</x><y>160</y><width>171</width><height>121</height></rect></property><property name="styleSheet"><string notr="true">QPushButton { background-color: rgb(239, 41, 41); border: none}QPushButton:hover { background-color: rgb(114, 159, 207);}</string></property><property name="text"><string>开启线程</string></property></widget><!-- 结束线程按钮 --><widget class="QPushButton" name="pushButton_2"><property name="geometry"><rect><x>420</x><y>160</y><width>171</width><height>121</height></rect></property><property name="styleSheet"><string notr="true">QPushButton { background-color: rgb(239, 41, 41); border: none}QPushButton:hover { background-color: rgb(114, 159, 207);}</string></property><property name="text"><string>结束线程</string></property></widget></widget><resources/><connections/>
</ui>
3. Qt多线程编程详解
3.1 多线程基础概念
在Qt中,多线程编程主要涉及以下概念:
- 进程(Process):一个应用程序至少有一个进程
- 线程(Thread):一个进程至少有一个线程(主线程)
- 主线程:负责UI更新和事件处理的线程
- 工作线程:执行耗时操作的后台线程
3.2 QThread类详解
QThread
是Qt中用于创建和管理线程的核心类。主要特点包括:
- 继承自
QObject
,支持信号槽机制 - 提供了线程的生命周期管理
- 通过重写
run()
方法定义线程执行逻辑 - 支持线程间通信
3.2.1 QThread的主要方法
class QThread : public QObject
{
public:// 启动线程void start(Priority priority = InheritPriority);// 等待线程结束bool wait(unsigned long time = ULONG_MAX);// 强制终止线程(不推荐)void terminate();// 退出线程void quit();// 检查线程是否正在运行bool isRunning() const;// 检查线程是否已完成bool isFinished() const;// 获取线程IDQt::HANDLE currentThreadId();// 线程睡眠(静态方法)static void sleep(unsigned long secs);static void msleep(unsigned long msecs);static void usleep(unsigned long usecs);protected:// 线程执行函数,需要重写virtual void run();signals:// 线程开始信号void started();// 线程完成信号void finished();
};
3.2.2 线程优先级
QThread
支持设置线程优先级:
enum Priority {IdlePriority, // 空闲优先级LowestPriority, // 最低优先级LowPriority, // 低优先级NormalPriority, // 正常优先级(默认)HighPriority, // 高优先级HighestPriority, // 最高优先级TimeCriticalPriority, // 时间关键优先级InheritPriority // 继承优先级
};// 使用示例
thread->start(QThread::HighPriority);
3.3 线程创建方式
3.3.1 继承QThread类(本项目使用的方式)
class MyThread : public QThread
{Q_OBJECTprotected:void run() override {// 线程执行逻辑// 这里的代码在新线程中执行}
};// 使用
MyThread *thread = new MyThread;
thread->start();
3.3.2 使用QObject + moveToThread(推荐方式)
class Worker : public QObject
{Q_OBJECTpublic slots:void doWork() {// 工作逻辑emit resultReady(result);}signals:void resultReady(const QString &result);
};// 使用
QThread *thread = new QThread;
Worker *worker = new Worker;worker->moveToThread(thread);connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MainWindow::handleResults);
connect(worker, &Worker::resultReady, worker, &QObject::deleteLater);
connect(worker, &Worker::resultReady, thread, &QThread::quit);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);thread->start();
3.3.3 使用QtConcurrent(简单任务)
#include <QtConcurrent>
#include <QFuture>// 执行简单函数
QFuture<void> future = QtConcurrent::run([](){// 后台执行的代码
});// 执行有返回值的函数
QFuture<int> future = QtConcurrent::run([](){ return 42;
});
int result = future.result();
3.4 线程同步与通信
3.4.1 信号槽机制
class WorkerThread : public QThread
{Q_OBJECTprotected:void run() override {for (int i = 0; i < 100; ++i) {emit progressChanged(i);msleep(100);}emit workFinished();}signals:void progressChanged(int value);void workFinished();
};// 在主线程中连接信号槽
connect(workerThread, &WorkerThread::progressChanged, progressBar, &QProgressBar::setValue);
connect(workerThread, &WorkerThread::workFinished, this, &MainWindow::onWorkFinished);
3.4.2 互斥锁(QMutex)
#include <QMutex>
#include <QMutexLocker>class ThreadSafeCounter
{
public:void increment() {QMutexLocker locker(&mutex); // 自动加锁和解锁++count;}int value() const {QMutexLocker locker(&mutex);return count;}private:mutable QMutex mutex;int count = 0;
};
3.4.3 等待条件(QWaitCondition)
#include <QWaitCondition>
#include <QMutex>QMutex mutex;
QWaitCondition condition;
bool dataReady = false;// 生产者线程
void producer() {QMutexLocker locker(&mutex);// 生产数据dataReady = true;condition.wakeOne(); // 唤醒等待的线程
}// 消费者线程
void consumer() {QMutexLocker locker(&mutex);while (!dataReady) {condition.wait(&mutex); // 等待条件满足}// 消费数据
}
3.4.4 读写锁(QReadWriteLock)
#include <QReadWriteLock>
#include <QReadLocker>
#include <QWriteLocker>class ThreadSafeMap
{
public:QString value(const QString &key) const {QReadLocker locker(&lock); // 读锁return map.value(key);}void setValue(const QString &key, const QString &value) {QWriteLocker locker(&lock); // 写锁map[key] = value;}private:mutable QReadWriteLock lock;QMap<QString, QString> map;
};
4. 实现步骤详解
4.1 创建自定义线程类
- 继承
QThread
类:
class MyThread : public QThread
{Q_OBJECT // 必须包含,支持信号槽public:MyThread(QObject *parent = nullptr) : QThread(parent) {}protected:void run() override {// 线程执行逻辑}
};
- 重写
run()
方法:
void MyThread::run()
{// 这里的代码在新线程中执行// 可以执行耗时操作,不会阻塞主线程qDebug() << "线程开始执行";// 模拟耗时操作for (int i = 0; i < 10; ++i) {sleep(1); // 睡眠1秒qDebug() << "执行进度:" << (i + 1) * 10 << "%";}qDebug() << "线程执行完毕";
}
4.2 启动和管理线程
- 创建线程对象:
MyThread *thread = new MyThread(this);
- 启动线程:
thread->start(); // 调用start()会自动调用run()方法
- 等待线程结束:
thread->wait(); // 阻塞等待线程结束
- 检查线程状态:
if (thread->isRunning()) {qDebug() << "线程正在运行";
}if (thread->isFinished()) {qDebug() << "线程已完成";
}
4.3 线程生命周期管理
- 自动销毁:
void MyThread::run()
{// 执行逻辑deleteLater(); // 线程结束后自动销毁
}
- 手动销毁:
connect(thread, &QThread::finished, thread, &QObject::deleteLater);
- 优雅退出:
class MyThread : public QThread
{
private:bool m_stop = false;public:void stop() { m_stop = true; }protected:void run() override {while (!m_stop) {// 执行工作if (m_stop) break;msleep(100);}}
};
5. 常用功能示例
5.1 带进度更新的线程
class ProgressThread : public QThread
{Q_OBJECTprotected:void run() override {for (int i = 0; i <= 100; ++i) {emit progressChanged(i);msleep(50); // 模拟工作}emit workCompleted();}signals:void progressChanged(int value);void workCompleted();
};// 使用
ProgressThread *thread = new ProgressThread;
connect(thread, &ProgressThread::progressChanged, progressBar, &QProgressBar::setValue);
connect(thread, &ProgressThread::workCompleted, this, &MainWindow::onWorkCompleted);
thread->start();
5.2 文件处理线程
class FileProcessThread : public QThread
{Q_OBJECTpublic:FileProcessThread(const QString &filePath) : m_filePath(filePath) {}protected:void run() override {QFile file(m_filePath);if (!file.open(QIODevice::ReadOnly)) {emit error("无法打开文件");return;}QTextStream stream(&file);QString content = stream.readAll();// 处理文件内容QString processedContent = processContent(content);emit contentProcessed(processedContent);}private:QString m_filePath;QString processContent(const QString &content) {// 模拟内容处理return content.toUpper();}signals:void contentProcessed(const QString &content);void error(const QString &message);
};
5.3 网络请求线程
class NetworkThread : public QThread
{Q_OBJECTpublic:NetworkThread(const QString &url) : m_url(url) {}protected:void run() override {QNetworkAccessManager manager;QNetworkRequest request(m_url);QNetworkReply *reply = manager.get(request);QEventLoop loop;connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);loop.exec();if (reply->error() == QNetworkReply::NoError) {QByteArray data = reply->readAll();emit dataReceived(data);} else {emit error(reply->errorString());}reply->deleteLater();}private:QString m_url;signals:void dataReceived(const QByteArray &data);void error(const QString &message);
};
5.4 定时器线程
class TimerThread : public QThread
{Q_OBJECTpublic:TimerThread(int interval) : m_interval(interval), m_stop(false) {}void stop() { m_stop = true; }protected:void run() override {while (!m_stop) {emit timeout();msleep(m_interval);}}private:int m_interval;bool m_stop;signals:void timeout();
};
6. 常见问题与解决方案
6.1 UI更新问题
问题:在工作线程中直接更新UI控件导致程序崩溃。
原因:Qt的UI控件只能在主线程中更新。
解决方案:使用信号槽机制在主线程中更新UI。
// 错误做法(在工作线程中)
void WorkerThread::run()
{label->setText("更新文本"); // 错误!会导致崩溃
}// 正确做法
void WorkerThread::run()
{emit textChanged("更新文本"); // 发送信号
}// 在主线程中连接信号槽
connect(workerThread, &WorkerThread::textChanged, label, &QLabel::setText);
6.2 内存泄漏问题
问题:线程对象没有正确释放导致内存泄漏。
解决方案:
// 方案1:使用deleteLater()
connect(thread, &QThread::finished, thread, &QObject::deleteLater);// 方案2:在run()方法中调用deleteLater()
void MyThread::run()
{// 执行逻辑deleteLater();
}// 方案3:手动管理
if (thread->isFinished()) {delete thread;thread = nullptr;
}
6.3 线程阻塞问题
问题:主线程等待工作线程完成导致界面卡死。
解决方案:避免在主线程中使用wait()
方法。
// 错误做法
void MainWindow::startWork()
{thread->start();thread->wait(); // 阻塞主线程,界面卡死
}// 正确做法
void MainWindow::startWork()
{connect(thread, &QThread::finished, this, &MainWindow::onWorkFinished);thread->start();
}void MainWindow::onWorkFinished()
{// 处理工作完成后的逻辑
}
6.4 线程安全问题
问题:多个线程同时访问共享数据导致数据竞争。
解决方案:使用互斥锁保护共享数据。
class ThreadSafeClass
{
public:void setValue(int value) {QMutexLocker locker(&mutex);m_value = value;}int getValue() const {QMutexLocker locker(&mutex);return m_value;}private:mutable QMutex mutex;int m_value = 0;
};
6.5 线程终止问题
问题:使用terminate()
强制终止线程可能导致资源泄漏。
解决方案:使用标志位实现优雅退出。
class GracefulThread : public QThread
{
public:void requestStop() { m_stopRequested = true; }protected:void run() override {while (!m_stopRequested) {// 执行工作if (m_stopRequested) break;msleep(100);}}private:volatile bool m_stopRequested = false;
};
7. 高级应用
7.1 线程池
#include <QThreadPool>
#include <QRunnable>class Task : public QRunnable
{
public:void run() override {// 执行任务qDebug() << "任务执行中...";}
};// 使用线程池
QThreadPool *pool = QThreadPool::globalInstance();
pool->start(new Task);
7.2 生产者-消费者模式
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>template<typename T>
class ThreadSafeQueue
{
public:void enqueue(const T &item) {QMutexLocker locker(&mutex);queue.enqueue(item);condition.wakeOne();}T dequeue() {QMutexLocker locker(&mutex);while (queue.isEmpty()) {condition.wait(&mutex);}return queue.dequeue();}private:QQueue<T> queue;QMutex mutex;QWaitCondition condition;
};class Producer : public QThread
{
protected:void run() override {for (int i = 0; i < 100; ++i) {queue.enqueue(i);msleep(10);}}private:ThreadSafeQueue<int> &queue;
};class Consumer : public QThread
{
protected:void run() override {while (true) {int item = queue.dequeue();qDebug() << "消费:" << item;}}private:ThreadSafeQueue<int> &queue;
};
7.3 异步任务管理器
class TaskManager : public QObject
{Q_OBJECTpublic:void addTask(QRunnable *task) {QThreadPool::globalInstance()->start(task);}void waitForAllTasks() {QThreadPool::globalInstance()->waitForDone();}int activeThreadCount() const {return QThreadPool::globalInstance()->activeThreadCount();}void setMaxThreadCount(int count) {QThreadPool::globalInstance()->setMaxThreadCount(count);}
};