多线程中使用QTimer
我们可能在Qt的多线程中使用QTimer中都会遇到一个错误:
Cannot create children for a parent that is in a different thread.
或者
QObject::startTimer: Timers cannot be started from another thread
QTimer定时器不能在不同的线程中启动。
出现这个主要原因是没有很好的将主线程和子线程进行区分。
我们看以下的多线程代码:(在Qt中的多线程)
这个是定义一个类继承自QThread,并且重写其中的虚函数run。之后,启动线程run函数就在子线程中运行了。
1 #ifndef MYTHREAD_H
2 #define MYTHREAD_H
3 #include <QThread>
4 #include <QTimer>
5
6 class MyThread : public QThread
7 {
8 Q_OBJECT
9 public:
10 MyThread();
11 virtual void run()override;
12 QTimer* timer;
13
14 signals:
15 void TestThread_Signal(); //自定义的信号
16 public slots:
17 void TestThread_Slot();
18 void wode();
19 };
20
21 #endif // MYTHREAD_H
错误代码1:
QObject::startTimer: Timers cannot be started from another thread
出现这个原因:主要是在主线程中进行Qtimer在堆中的内存分配,因此,该定时器属于同一个线程,如果在子线程中进行start,那么就会在别的线程中开启主线程中的定时器。Qt不允许这样操作。
1 MainWindow::MainWindow(QWidget *parent) :
2 QMainWindow(parent),
3 ui(new Ui::MainWindow)
4 {
5 ui->setupUi(this);
6 qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
7 // t1.moveToThread(&t1);
8 t1.timer = new QTimer();
9 t1.timer->setInterval(90);
10 t1.start();
11 }
12 void MyThread::run()
13 {
14 connect(timer, &QTimer::timeout, this, &MyThread::wode);
15 timer->start();
16 this->exec();//这个是Qt的消息循环,只有加这个才可以监听信号
17 }
更改代码1:
将定时器的在堆中的函数
1 MainWindow::MainWindow(QWidget *parent) :
2 QMainWindow(parent),
3 ui(new Ui::MainWindow)
4 {
5 ui->setupUi(this);
6 qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
7 // t1.moveToThread(&t1);
8 t1.start();
9 }
10 void MyThread::run()
11 {
12 // connect(&timer,&QTimer::timeout,this,[](){qDebug()<<"Timer goes";});
13 //不能加this
14 timer = new QTimer();
15 timer->setInterval(90);
16 connect(timer, &QTimer::timeout, this, &MyThread::wode);
17 timer->start();
18 //一定要有exec()
19 this->exec();
20 }
运行结果:
可以看出槽函数在主线程中运行,这也就说明了t1对象属于主线程。
错误代码2:
经历了上面的那个例子,我们可以看出主线程和子线程的区别之处。那么,我们根据下面这个例子再forward。。。
啊哦~~~报警告了。
Cannot create children for a parent that is in a different thread.
这是为什么呢?
经过我们之前的分析,t1也即是那个线程类对象在主线程中,只有run函数体的部分属于子线程,那么,QTimer现在在子线程new对象,QTimer指向的内存即是子线程中,
(注意:timer是在主线程中,因为它是栈区的。)
你却将this(t1)传为它的父对象,怎么可能不报错呢???
因此,这既是有的博客说的千万不要加this的用意之处。我们可以通过以下代码进行测试:
1 if(this->thread() == timer->thread())
2 {
3 qDebug()<<"same";
4 };
1 MainWindow::MainWindow(QWidget *parent) :
2 QMainWindow(parent),
3 ui(new Ui::MainWindow)
4 {
5 ui->setupUi(this);
6 qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
7 // t1.moveToThread(&t1);
8 t1.start();
9 }
10 void MyThread::run()
11 {
12 // connect(&timer,&QTimer::timeout,this,[](){qDebug()<<"Timer goes";});
13 //不能加this
14 timer = new QTimer(this);
15 timer->setInterval(90);
16 connect(timer, &QTimer::timeout, this, &MyThread::wode);
17 timer->start();
18 //一定要有exec()
19 this->exec();
20 }
更改代码2:
用到了movetoThread函数进行。
1 MainWindow::MainWindow(QWidget *parent) :
2 QMainWindow(parent),
3 ui(new Ui::MainWindow)
4 {
5 ui->setupUi(this);
6 qDebug()<<"Main ThreadID"<< this->thread()->currentThreadId();
7 t1.moveToThread(&t1);
8 t1.start();
9 }
10 void MyThread::run()
11 {
12 timer = new QTimer(this);
13 if(this->thread() == timer->thread())
14 {
15 qDebug()<<"same";
16 };
17 timer->setInterval(90);
18 connect(timer, &QTimer::timeout, this, &MyThread::wode);
19 timer->start();
20 //一定要有exec()
21 this->exec();
22 }
运行结果:我们可以看出当通过moveToThread函数之后,我们就可以进行。
注意:从测试案例中我们可以看出。槽函数也是打印的子线程的线程号,因此,moveToThread函数会将t1移动到子线程中,new定时器的适合加this也问题不大了。因为,父子对象处于一个线程了。
经过上面的分析,我们对子线程和主线程的区分已经了解了。因此,在多线程中使用信号和槽就很容易了。
两点需要注意:
1、需要添加Q_OBJECT宏
2、需要exec()来监听信号