本文讨论的是基于qt框架实现多线程的核心QThread类,该类的是QObejct的一个子类,是方式有两种:
class MyThread:public QThread
{
private:
void run() //线程执行函数
{
....
}
}
采用这种方式实现的多线程一定要重新实现基类的run函数,线程的启动方式,通过调用start方法来触发:
myThread th;
th.start(); //该函数会触发run函数调用,而默认的run函数则会调用exec开启事件循环
此种方式下线程如何退出呢?
答:run函数(线程函数)执行完毕后,线程就会退出,如果run函数是一个死循环,则线程不会退出。
需要注意的是此种方式下由于重写了run函数,并不会开启事件循环。
此种方式实现多线程的思想与第一种不一样,该种方式不需要继承QThread类,并且此种方式仅能通过信号槽的方式来触发对象的函数执行,如果直接调用对象方法,那么对象方法则会运行在调用线程,不会在对应的线程执行。
此种方式的思想是创建一个对象,对象中包含一些方法,这些方法可以绑定到不同的线程,实现了通过一个对象可以包含多个线程函数的方法。首先介绍此种方法的使用步骤:
1、定义对象,该对象可以包含多个线程函数
//对象类
class Mywork : public QObject
{
Q_OBJECT
public:
explicit Mywork( QObject *parent = nullptr); //构造函数
~Mywork();
.....
public slots:
void task_one(); //任务函数1
void task_sencond(); //任务函数2
.....
};
2、绑定对象至线程
myWork work = new myWork(); //不能设置父对象,如果设置了父对象,则不能movetothread,想看movetothread的帮助手册
QThread* thread = new QThread(this);
work->moveToThread(thread); //将work对象绑定至thread线程
connect(thread, SIGNAL(started()), work, SLOT(task_one())); //绑定信号槽
//绑定第二个方法,一个对象类可以设置多个线程的执行函数
myWork work2 = new myWork(); //不能设置父对象,如果设置了父对象,则不能movetothread,想看movetothread的帮助手册
QThread* thread = new QThread(this);
work2->moveToThread(thread); //将work对象绑定至thread线程
connect(thread, SIGNAL(started()), work2, SLOT(task_sencond())); //绑定信号槽
3、启动子线程
thread->start();//该函数会调用run函数,默认的run函数则会调用exec函数,开启事件循环
thread2->start(); //同上
通过上面的步骤就完成了movetothread的多线程,其中task_one方法和task_second方法等运行在指定的线程中, 需要注意的是此种方式实现的的多线程只能通过信号槽的方式来触发。这句话该如何理解呢?若要保证线程函数或者任务函数在指定线程执行,则不能通过直接调用的方式来执行线程函数,其它线程需要通过发送信号的方式告知myWork对象执行对应的任务函数,即这个任务函数只有以槽函数的形式执行的时候,才会在对应的线程执行。
QThread类的run函数默认为开启exec(事件循环),如果采用第一种方式的话重写run函数的方法则不会开启事件循环。原因见下面的源码:
QThreadPrivate::start()
{
emit thr->started(QThread::QPrivateSignal());
thr->run();
}
通过上面的代码可以看到start方法的调用触发了run函数的调用
run函数的源码实现:
void QThread::run()
{
(void) exec();
}
void QThread::exec()
{
QEventLoop eventLoop;
int returnCode = eventLoop.exec();
return returnCode;
}
可以看到run函数内部调用了exec开启了事件循环。
注意事项:需要注意的是采用此种方式实现的多线程,在线程退出时需要调用quit或者exit函数,因为此种方式本质上是通过事件循环的方式,通过事件队列来实现函数执行在指定线程,如果要退出此种方式,那么显而易见的方式就是退出事件循环。
一般的退出方式如下:
thread->quit(); //退出线程
thread->wait(); //等待线程退出
如果不采用上面的方式退出线程则可能提示下面的错误:
QThread: Destroyed while thread is still running
//造成这个错误的原因是直接退出应用程序,但是子线程并没有退出,解决方法就是调用quit和wait方法退出线程
注意: 千万别忘记退出线程。否则子线程可能会继续执行。
QT线程之 moveToThread() 只能用信号槽方式触发_qwidget movetothread_立冲君也的博客-CSDN博客