目录
第1章 简介
1.1 多线程的目的
1.2 QThread多线程使用方法
1.3 QT支持多线的步骤
第2章 QThread
2.1 概述
2.2 moveToThread
QThread类提供了一个与平台无关的管理线程的方法。
在Qt中建立线程的主要目的就是为了用线程来处理那些耗时的后台操作,比如大量运算,复制大文件,网络传输等。
QT(也称为Qt框架)是一个用于开发跨平台应用程序的C++库。它提供了丰富的功能,包括多线程支持。
多线程是一种在同一时间处理多个任务的技术,它可以改善应用程序的响应性和性能。
1. 自定义Thread继承QThread类
2. QObject::moveToThread()
在QT中使用多线程可以通过以下几个步骤来实现:
引入Qt多线程相关的类:在你的代码中引入/包含以下类的头文件:QThread、QRunnable、QThreadPool、QObject等。
创建一个继承自QThread或QRunnable的类:这个类将被用来执行需要在后台线程中运行的任务。如果你需要更多的灵活性和控制,可以选择继承自QThread,否则继承自QRunnable会更简单。
重写run()方法:在你的自定义类中重写run()方法,并在其中实现你的任务逻辑。
创建并启动线程:在主线程中创建你的自定义类的实例,然后调用start()方法来启动线程。这将自动调用你的run()方法。
以下是一个简单的示例代码,展示了如何在QT中使用多线程:
#include
#include
class MyThread : public QThread
{
Q_OBJECT
public:
void run() override
{
// 执行你的任务逻辑
qDebug() << "Hello from thread";
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
MyThread thread;
thread.start();
// 主线程继续执行其他任务
qDebug() << "Hello from main thread";
return app.exec();
}
在上面的例子中,我们创建了一个名为MyThread的类,继承自QThread。我们重写了run()方法,其中打印了一条消息。在main()函数中,我们创建了一个MyThread实例,并调用它的start()方法来启动线程。同时,主线程也继续执行其他任务。当运行这个程序时,你会看到两条消息,一条来自主线程,一条来自子线程。
需要注意的是,当使用多线程编程时,需要小心处理线程间的共享数据访问,避免出现竞争条件和其他线程相关的问题。QT提供了一些同步工具,如互斥锁和信号量,来帮助你实现线程间的安全数据访问。
Qt提供了强大的多线程支持,使开发者能够轻松地在应用程序中实现并发和并行处理。
以下是Qt多线程支持的一些重要组件和功能:
QThread类:QThread是Qt提供的多线程编程的基础类。开发者可以通过继承QThread类来创建自己的线程类,并实现run()函数来定义线程的执行逻辑。
信号和槽机制:Qt的信号和槽机制是一个强大的线程间通信机制。线程间的对象通信可以通过信号和槽的方式来实现,无需直接操作共享数据。这种机制可以有效地降低多线程编程的复杂性。
QMutex类:QMutex是一个互斥锁类,用于保护共享数据的访问。通过QMutex,你可以确保在同一时间只有一个线程可以访问被保护的代码区域,从而避免竞态条件。
QWaitCondition类:QWaitCondition是一个条件变量类,用于线程间的等待和唤醒操作。通过QWaitCondition,你可以实现线程的等待直到某个条件满足,并在条件满足时唤醒线程。
QThreadPool类:QThreadPool是负责管理和调度线程的线程池类。通过QThreadPool,你可以将任务提交到池中,线程池会自动为你管理线程的生命周期和任务的执行。
除了上述的核心组件,Qt还提供了其他一些辅助类和工具,如QReadWriteLock(读写锁)、QSemaphore(信号量)、 QtConcurrent(并行编程框架)等,这些类和工具都可以帮助你更好地处理多线程编程。
在使用Qt多线程时,需要注意以下几点:
避免对共享数据的直接访问:尽量不要直接操作共享数据,而是通过信号和槽或使用互斥锁等机制来确保线程安全。
避免长时间阻塞主线程:如果在主线程中有耗时的操作,应该将其放入后台线程以避免阻塞主线程,保证应用程序的响应性。
小心使用线程间传递的数据:在线程间传递数据时需要注意数据的有效性和生命周期,避免出现悬空指针或无效引用等问题。
Qt的多线程支持可以帮助你更轻松地编写多线程应用程序,并在并发和并行处理方面提供了很大的便利性。通过合理地使用Qt的多线程功能,你可以提高应用程序的性能和响应性,并更好地利用多核处理器的计算能力。
QThread是Qt提供的一个类,用于在应用程序中创建和管理线程。
它封装了底层的线程操作,使多线程编程变得更加方便和可管理。
以下是一些使用QThread的一般步骤和常见用法:
class MyThread : public QThread {
public:
void run() override {
// 编写线程逻辑
}
};
MyThread* thread = new MyThread();
thread->start();
connect(someObject, &SomeObject::someSignal, thread, &MyThread::someSlot);
// 或者反向连接
connect(thread, &MyThread::someSignal, someObject, &SomeObject::someSlot);
thread->wait(); // 等待线程结束
thread->quit(); // 请求线程退出
thread->terminate(); // 强制终止线程
if (thread->isRunning()) {
// 线程正在运行
}
QThread还提供了其他一些辅助功能,例如设置线程的优先级、设置线程栈的大小、事件循环和事件处理等。
需要注意的是,直接继承QThread并重写run()函数是一种使用QThread的传统方式。但是从Qt 5.2版本开始,Qt提供了一种更简单和更推荐的方式来处理多线程,即使用Qt的信号和槽机制与QRunnable和QThreadPool或Qt Concurrent组合使用。
希望这些信息对使用QThread类来创建和管理线程有所帮助。如果有任何进一步的问题,请随时提问。
QThread类是Qt中用于创建和管理线程的类,它提供了许多成员函数来管理线程的行为和状态。
以下是QThread类中一些常见的成员函数:
start()
:启动线程,使其以独立线程的方式开始执行run()函数中的代码。
run()
:需要在子类中重新实现的虚函数,定义线程执行的主要代码逻辑。
wait()
:阻塞当前线程,直到线程执行完成。
sleep()
:使当前线程睡眠指定的毫秒数。
terminate()
:终止线程的执行。这是一种比较粗暴的方法,通常不推荐使用。
quit()
:停止线程的事件循环,推出线程执行。通常与event loop(事件循环)一起使用。
msleep()
:使当前线程睡眠指定的毫秒数。
usleep()
:使当前线程睡眠指定的微秒数。
isRunning()
:判断线程是否正在执行。
currentThreadId()
:返回当前线程的唯一标识符。
priority()
/ setPriority()
:获取或设置线程的优先级。
finished()
:当线程执行完成时发出的信号。
started()
:当线程开始执行时发出的信号。
finished()
:在线程执行完成后,由QThread对象发出的信号。
moveToThread(QThread* thread)
:将对象移动到指定的线程中运行。
这只是QThread类中的一些常见成员函数,通过使用这些函数,可以管理线程的执行、状态和行为。根据具体的应用需求,可能会使用更多的QThread成员函数来完成线程的管理和操作。
moveToThread
3.1 概述
moveToThread()
是一个Qt中的成员函数,用于将对象移到指定的线程中执行。
它允许您在多线程应用程序中管理对象的线程执行上下文。
以下是moveToThread()
函数的语法和用法:
void QObject::moveToThread(QThread* thread)
thread
:指向目标线程的指针。moveToThread()
函数的作用是将调用该函数的对象与指定的线程相关联。
这意味着对象的方法和事件将在目标线程的上下文中执行,而不是在调用线程(创建对象的线程)的上下文中执行。
以下是一个使用moveToThread()
函数的示例:
#include
#include
#include
class Worker : public QObject {
Q_OBJECT
public slots:
void doWork() {
qDebug() << "Worker thread:" << QThread::currentThread();
// 执行耗时操作
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
qDebug() << "Main thread:" << QThread::currentThread();
// 创建一个新的管理线程上下文的对象
QThread* workerThread = new QThread;
// 创建一个可以task对象,该对象可以执行某种操作,比如while循环的函数
Worker* worker = new Worker;
// 将Worker对象移到 workerThread 线程
// 如果没有这行代码,则worker的task在创建worker线程中执行,而不是目标线程上下文中执行
worker->moveToThread(workerThread);
// 当线程启动时,执行 Worker 的 doWork() 槽函数
// 线程QThread也是类的对象,因此,可以通过槽函数和信号在对象间通信:
// 在QThread对象中执行另一个需要循环执行的对象的成员函数(while循环)
QObject::connect(workerThread, &QThread::started, worker, &Worker::doWork);
//在QThread对象的上下文中发送一个started信号,在QThread线程对象的上下文中执行worker对象中的槽函数,该槽函数是一个while循环执行的函数。
workerThread->start();
return app.exec();
}
在上面的示例中,我们创建了一个Worker
类,该类继承自QObject
。Worker
类中定义了一个doWork()
方法用于执行耗时的工作。
在主函数中,我们创建了一个worker
对象和一个workerThread
线程对象。然后,使用moveToThread()
将worker
对象移动到workerThread
线程中。这样,worker
对象的doWork()
方法将在workerThread
线程中执行。
通过连接workerThread
的started
信号与worker
的doWork()
槽函数,当线程启动时,doWork()
方法将自动被调用。
需要注意的是,moveToThread()
函数只能在对象所属的线程中调用,否则会导致未定义的行为。因此,建议在创建对象后尽早调用moveToThread()
函数来确保对象在预期的线程中执行。
moveToThread()
函数在多线程编程中非常有用,它允许您将对象移动到适当的线程,并在该线程中执行相关的方法和事件处理。这有助于将耗时的操作与UI交互分开,保持应用程序的响应性。
moveToThread创建线程的比较
4.1 代码实例
#include "mainwindow.h"
#include
#include
#include
#include
#include
class MyThread : public QThread {
public:
void run() override {
qDebug() << "Run Thread started";
// 在这里执行耗时任务
int i = 0;
while(1)
{
sleep(3);
qDebug() << i << ": run thread:" << QThread::currentThread();
i++;
}
qDebug() << "Run Thread finished";
}
};
class MyWorker : public QObject {
Q_OBJECT
public slots:
void doWork() {
qDebug() << "doWork Thread started";
while(1){
qDebug() << "Worker thread:" << QThread::currentThread();
// 执行耗时操作
QThread::sleep(3);
}
qDebug() << "doWork Thread finished";
}
};
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
qDebug() << "Main thread started: " << QThread::currentThread() ;
// 创建并启动run线程1
MyThread thread_run1;
thread_run1.start();
// 创建并启动run线程2
MyThread thread_run2;
thread_run2.start();
//创建一个管理线程上下文的对象
QThread* workerThread1 = new QThread;
//创建一个task对象
MyWorker* myworker1 = new MyWorker;
// 将 Worker 对象移到 workerThread 线程
//myworker1->moveToThread(workerThread1);
// 当线程启动时,执行 Worker 的 doWork() 槽函数
// 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
QObject::connect(workerThread1, &QThread::started, myworker1, &MyWorker::doWork);
workerThread1->start();
//创建一个管理线程上下文的对象
QThread* workerThread2 = new QThread;
//创建一个task对象
MyWorker* myworker2 = new MyWorker;
// 将 Worker 对象移到 workerThread 线程
myworker2->moveToThread(workerThread2);
// 当线程启动时,执行 Worker 的 doWork() 槽函数
// 线程也是一个对象,因此可以使用对象之间的通信机制,进行通信
QObject::connect(workerThread2, &QThread::started, myworker2, &MyWorker::doWork);
workerThread2->start();
qDebug() << "Main thread finished" << QThread::currentThread() ;
w.show();
return a.exec();
}
4.2 输出结果
Main thread started: QThread(0x9c7770)
Main thread finished QThread(0x9c7770)
Run Thread started
Run Thread started
doWork Thread started
Worker thread: QThread(0x30593d0)
doWork Thread started
Worker thread: QThread(0x9c7770) //没有myworker1->moveToThread(workerThread1),因此,在主线程上下文中执行,因此,执行比较缓慢,滞后于workerThread2上下文的myworker2
0 : run thread: QThread(0x8bfc30)
0 : run thread: QThread(0x8bfc20)
Worker thread: QThread(0x30593d0)
Worker thread: QThread(0x9c7770)
1 : run thread: QThread(0x8bfc30)
1 : run thread: QThread(0x8bfc20)
Worker thread: QThread(0x30593d0)
Worker thread: QThread(0x9c7770)