1 为什么需要多线程
2 Qt中使用多线程的一些注意事项
3 QThread类
3.1 QThread类的主要接口
3.2 线程的优先级
4 通过继承QThread类实现多线程
5 从QObject类进行派生实现多线程
5 小结
2、子线程和主线程之间的数据传递主要通过信号槽机制进行。
3、子线程用于负责非UI部分的后台逻辑,不可操作窗口中的对象。
在使用时,通常会定义一个继承QThread类的自定义类,重定义虚函数run()的实现;用一个从该自定义类实例化的对象来管理一个线程;在主线程中通过对象的start()接口来启动线程(start()接口底层会调用run()实现);在run()实现中调用exit()或者quit()能够结束线程的事件循环,在主线程中调用terminate()可以强制结束线程。
但也可以通过从QObject类进行派生的方式来实现多线程,这种方式使用起来会更加灵活,但是在代码实现上会更复杂一些。
1、[公共函数] 判断线程是否结束:bool isFinished()
2、[公共函数] 判断线程是否正在运行:bool isRunning()
3、[公共函数] 获得线程的优先级:Priority priority()
4、[公共函数] 设置线程的优先级:void setPriority(Priority priority)
5、[公共函数] 退出事件循环:void exit(int returnCode = 0)
6、[公共函数] 阻塞线程执行直到线程结束,或者time 毫秒:void wait(unsigned long time)
7、[公共槽函数] 退出线程事件循环:void quit()
8、[公共槽函数] 根据priority,开始调度、执行线程:void start(Priority priority)
9、[公共槽函数] 终止线程:void terminate()
10、[信号] 在线程快结束完成时发射:void finished()
11、[信号] 在线程调用run()实现前发射:void started()
12、[静态公共函数] 获得系统可运行的线程的数量:int idealThreadCount()
13、[静态公共函数] 强制当前线程休眠msecs:int msleep(unsigned long msecs)
14、[静态公共函数] 强制当前线程休眠secs:int sleep(unsigned long secs)
15、[静态公共函数] 强制当前线程休眠usecs:int usleep(unsigned long usecs)
16、[保护函数] 线程执行体虚函数:virtual void run()
17、[保护函数] 进入线程事件循环:int exec()
QThread::IdelPriority
QThread::LowestPriority
QThread::LowPriority
QThread::NormalPriority
QThread::HighPriority
QThread::HigestPriority
QThread::InheritPriority
这种方式的优势在于:
1、可以直接重写run()函数,实现线程的执行逻辑。
2、可以通过start()函数启动线程,比较直观和简单。
但这种方式的劣势也很明显:
1、在多线程中,直接操作QObject派生类的成员可能会导致线程安全问题,需要进行额外的同步处理。
2、QThread类的创建和销毁需要小心处理,避免资源泄漏和线程未正常退出的情况。
在具体的操作上,通常要经过如下步骤:
1、定义一个继承QThread类的自定义类。
class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
protected:
void run() override;
};
2、在自定义类中重写父类QThread的run()方法,在该实现中编写子线程的具体逻辑、处理、流程。
void MyThread::run()
{
// 执行多线程的工作逻辑
// ...
}
3、在主线程中,创建一个子线程对象,将自定义的子线程类实例化,并连接适当的信号槽来处理工作完成的事件。
MyThread* thread = new MyThread(this);
connect(thread, &MyThread::finished, this, &MainWindow::handleThreadFinished);
4、在主线程中,通过创建的子线程对象,调用start(),启动子线程。
thread->start();
5、在主线程中和子线程,通过信号槽处理线程之间的数据传递、处理逻辑。
6、在合适的时候,对线程的资源进行合理的释放
这种方式的优势在于:
1、QObject类提供了信号槽机制,方便多线程之间的通信和数据传递。
2、可以利用Qt的事件循环机制处理事件,例如定时器事件、网络事件等。
3、可以使用Qt提供的线程安全的容器和工具类,方便进行线程间的数据共享和同步。
但这种方式的劣势在于:
1、无法直接重写run()函数,需要使用QRunnable接口或QtConcurrent库来实现线程的执行逻辑。
2、对象的生命周期和线程的生命周期紧密相关,需要小心处理对象的创建和销毁,避免线程结束后对象仍然存在。
在具体的操作上,通常要经过如下步骤:
1、创建一个QObject类的派生类,作为多线程的工作对象
class MyWorker : public QObject
{
Q_OBJECT
public:
explicit MyWorker(QObject *parent = nullptr);
public slots:
void doWork();
signals:
void workFinished();
};
2、在创建的QObject类的派生类中,定义一个槽函数 doWork(),用于执行多线程的工作逻辑。
void MyWorker::doWork()
{
// 执行多线程的工作逻辑
// ...
// 工作完成后发射信号
emit workFinished();
}
3、在主线程中,创建一个不指定父对象的QThread对象thread,并将QObject派生类的对象worker移动到这个线程对象thread中。
QThread* thread = new QThread;
MyWorker* worker = new MyWorker;
worker->moveToThread(thread);
4、连接MyWorker对象的 workFinished() 信号到适当的槽函数,以处理工作完成的事件。
QObject::connect(worker, &MyWorker::workFinished, this, &MainWindow::handleWorkFinished);
启动线程并调用 doWork() 函数开始执行多线程工作。
thread->start();
QMetaObject::invokeMethod(worker, "doWork");
5、通过以上步骤,我们就可以从QObject类派生实现多线程,并利用信号槽机制进行线程间的通信和数据传递了。
注意,在这个例子中,我们将MyWorker对象移动到了新创建的线程中,并在主线程中通过信号槽机制连接工作完成的信号。这样,当 doWork() 函数执行完成后,会自动发射 workFinished() 信号,触发相应的槽函数执行。