一、QThread类介绍
Qt中实现多线程需要使用线程类QThread。
主要接口:start() 开始线程
虚函数:virtual void run() 线程的运行run函数。虚函数子类重新实现
信号函数:finished() 线程运行结束会发出此信号
二、多线程的2种实现方式
1、重写run函数
(1)定义线程类,继承自QThread
(2)在线程类内实现run()函数
(3)在需要创建子线程位置,创建定义的线程类对象 t
(4)调用start函数, t->start(),现成启动,run函数种代码开始运行
#include <QThread>
// WorkerThread 类继承自QThread
class WorkerThread : public QThread
{
Q_OBJECT
// 重写虚函数run函数,函数内代码在子线程中运行
void run() override {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result); // 现成结束发射信号
}
signals:
void resultReady(const QString &s);
};
void MyObject::startWorkInAThread()
{
// 创建自定义线程类对象
WorkerThread *workerThread = new WorkerThread(this);
// 绑定线程结束信号
connect(workerThread, &WorkerThread::resultReady, this, &MyObject::handleResults);
connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);
// 启动线程
workerThread->start();
}
2、借助moveToThread函数
// 定义线程工作类,槽函数doWork中代码在子线程中运行
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
/* ... here is the expensive or blocking operation ... */
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
// 启动子线程
class Controller : public QObject
{
Q_OBJECT
// 由于Worker 类没有继承QThread, 定义线程对象QThread
QThread workerThread;
public:
Controller() {
// 创建对象
Worker *worker = new Worker;
// 把对象移动到线程中
worker->moveToThread(&workerThread);
// 绑定线程结束信号
connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
// 信号槽关联。当信号operate被发射时,槽函数doWork的执行是在子线程中执行的。
connect(this, &Controller::operate, worker, &Worker::doWork);
// 绑定线程信号
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
// 线程启动,启动之后信号operate发射,doWork才会在子线程中运行
workerThread.start();
}
~Controller() {
workerThread.quit();
workerThread.wait();
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
(1)定义子线程处理类work,继承自QObject
(2)定义几个槽函数,槽函数实现线程需要处理的代码
(3)在需要运行子线程位置,创建work对象,创建QThread对象 t
(4)work->moveToThread(t);
(5)使用信号函数,关联work的槽函数
(6)t.start()开启线程
(7)发射信号,work槽函数开始在子线程中运行
三、两种实现方式对比
第一种方式
每次增加以一个线程,都会增加一个类
第二种方式
关联多个槽函数时,涉及到槽函数调用先后排队问题
四、多线程使用注意事项
1. 业务对象, 构造的时候不能指定父对象
2. 子线程中不能处理ui窗口(ui相关的类)
3. 子线程中只能处理一些数据相关的操作, 不能涉及窗口
如果需要把数据显示到UI,绑定信号与槽函数,发射信号,在主线程更新UI数据。
五、使用QThread实现多线程总结
优点:
- 控制灵活:
QThread
提供了丰富的接口,允许开发者对线程的生命周期和行为进行细粒度的控制。 - 资源共享: 可以方便地在主线程和工作线程之间共享数据,通过
moveToThread
方法将对象移至特定线程。 - 线程间通信: 可以使用
QObject
的信号和槽机制实现线程安全地通信。 - 适用于复杂应用: 对于需要精细控制线程的应用,
QThread
是一个很好的选择。
缺点:
- 编程复杂性: 需要手动管理线程的创建、执行和销毁,增加了编程的复杂性。
- 资源消耗: 每个线程都需要独立的栈空间,如果创建大量线程,可能会消耗较多的系统资源。
- 线程同步: 需要手动处理线程间的同步问题,如使用互斥锁(
QMutex
)等,增加了出错的可能性。
六、使用QtConcurrent
实现多线程
MyClass myObject;
// 定义监视线程对象,线程函数参数为int
QFutureWatcher<int> watcher;
// 关联线程完成信号,线程执行完后发射信号finished,handleFinished槽函数执行
connect(&watcher, SIGNAL(finished()), &myObject, SLOT(handleFinished()));
// Start the computation.
// 线程执行函数 ,runWork函数为MyClass 类中的public普通函数
QFuture<int> future = QtConcurrent::run(myObject, &MyClass::runWork,10);
// 启动线程
watcher.setFuture(future);
优点:
- 简化编程:
QtConcurrent
提供了简化的 API,如run
函数,可以很容易地在多线程中运行函数或算法。 - 易于使用: 对于简单的并行计算任务,
QtConcurrent
提供了一种更简洁和易于使用的方法。 - 自动管理线程:
QtConcurrent
会自动管理线程的创建和销毁,减少了开发者的负担。 - 支持并行模式:
QtConcurrent
支持不同的并行模式,如线程池模式,可以有效地利用系统资源。
缺点:
- 控制有限: 相比
QThread
,QtConcurrent
提供的控制接口较为有限,不适合需要精细控制线程的场景。 - 不适合I/O密集型任务:
QtConcurrent
主要适用于计算密集型任务,对于 I/O 密集型任务,可能不会带来太大的性能提升。 - 资源共享困难: 与
QThread
相比,QtConcurrent
在资源共享方面不如QThread
方便
七、总结
选择使用 QThread
还是 QtConcurrent
取决于具体的应用场景和需求。如果需要对线程进行细粒度的控制,或者应用较为复杂,QThread
是更好的选择。如果任务相对简单,或者希望简化并行编程的复杂性,QtConcurrent
是一个不错的选择。在实际开发中,也可以根据需要将两者结合使用。
关于面试的话会问到如何使用QThread类创建线程相关知识。