QTimer使用问题
1. 概述
在QT
中使用定时器QTimer
时,一不小心可能就会造成定时器失效的情况,这个问题的本质是QTimer
实例运行在了一个没有启用事件循环的线程中,从而造成其失效
2. 使用场景
- 主线程中使用
- 子线程中使用
2.1 注意
定时器的创建与使用要在同一个线程中,这个问题的演示会在2.2.2 QThread
小节中进行
2.2 主线程中使用
- 首先创建一个
QTimerInMainThread
类,内容如下:#ifndef QTIMERRUNINTHREAD_H #define QTIMERRUNINTHREAD_H #include <QObject> #include <QTimer> class QTimerInMainThread : public QObject { Q_OBJECT public: QTimerInMainThread(); ~QTimerInMainThread(); QTimer m_timer; int m_count; public slots: void timeoutHandle(); }; #endif // QTIMERRUNINTHREAD_H
- 实现
QTimerInMainThread
类#include "QTimerRunInThread.h" #include <QThread> #include <QDebug> QTimerInMainThread::QTimerInMainThread() : m_count(0) { QObject::connect(&m_timer,&QTimer::timeout,this,&QTimerInMainThread::timeoutHandle); m_timer.start(100); } QTimerInMainThread::~QTimerInMainThread() { m_timer.stop(); } void QTimerInMainThread::timeoutHandle() { qDebug() << "current thread id: " << QThread::currentThreadId() << "m_count: " << m_count++; }
- main.cpp
#include <QCoreApplication>
#include "QTimerObjectInQThread.h"
#include <QDebug>
#include <QThread>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id: " << QThread::currentThreadId() ;
/*
* 第一种方式:定时器运行在主线程中
* 直接主线程中启动一个定时器
*/
static QTimerInMainThread mainThreadTimer;
return a.exec();
}
2.3 子线程中使用
在QT
中线程的使用通常分为两种,接下来就以这两种进行举例。
2.3.1 moveToThread
- 创建
QTimerMoveToChildrenThread
类#ifndef QTIMERMOVETOCHILDRENTHREAD_H #define QTIMERMOVETOCHILDRENTHREAD_H #include <QObject> #include <QTimer> #include <QThread> class QTimerMoveToChildrenThread: public QObject { Q_OBJECT public: QTimerMoveToChildrenThread(); ~QTimerMoveToChildrenThread(); void start(); void stop(); QTimer m_timer; QThread m_timerThread; int m_count; signals: void startSignal(int msec); void stopSignal(); public slots: void timeoutHandle(); }; #endif // QTIMERMOVETOCHILDRENTHREAD_H
- 实现
QTimerMoveToChildrenThread
类#include "QTimerMoveToChildrenThread.h" #include <QDebug> QTimerMoveToChildrenThread::QTimerMoveToChildrenThread() : m_count(0) { // 更改定时器运行的线程 m_timer.moveToThread(&m_timerThread); // 定时器触发事件 connect(&m_timer,&QTimer::timeout,this,&QTimerMoveToChildrenThread::timeoutHandle); // 连接定时器启动槽函数 QObject::connect(this,static_cast<void (QTimerMoveToChildrenThread::*)(int)>(&QTimerMoveToChildrenThread::startSignal), &m_timer,static_cast<void (QTimer::*)(int)>(&QTimer::start)); // 连接定时器关闭槽函数 QObject::connect(this,&QTimerMoveToChildrenThread::stopSignal,&m_timer,&QTimer::stop); m_timerThread.start(); } QTimerMoveToChildrenThread::~QTimerMoveToChildrenThread() { stop(); m_timerThread.quit(); } void QTimerMoveToChildrenThread::start() { //向子线程内的定时器发送开启定时器信号 emit startSignal(100); } void QTimerMoveToChildrenThread::stop() { //向子线程内的定时器发送停止定时器信号 emit stopSignal(); } void QTimerMoveToChildrenThread::timeoutHandle() { // 这个在主线程中执行 qDebug() << "current thread id: " << QThread::currentThreadId() << "m_count: " << m_count++; }
- main.cpp
#include <QCoreApplication> #include "QTimerMoveToChildrenThread.h" #include <QDebug> #include <QThread> int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); qDebug() << "main thread id: " << QThread::currentThreadId() ; /* * 第二种方式:定时器运行在子线程中 * 将定时器移动到子线程中,外部发送启动信号启动子线程中的定时器 */ static QTimerMoveToChildrenThread moveToChildrenThreadTimer; moveToChildrenThreadTimer.start(); return a.exec(); }
2.3.2 QThread
- 创建
QTimerObjectInQThread
类
#ifndef QTIMEROBJECTINQTHREAD_H
#define QTIMEROBJECTINQTHREAD_H
#include <QObject>
#include <QThread>
#include <QTimer>
class QTimerObjectInQThread : public QThread
{
Q_OBJECT
public:
QTimerObjectInQThread();
~QTimerObjectInQThread();
QTimer* m_ptimer;
int m_count;
public slots:
void timeoutHandle();
protected:
virtual void run() override;
};
#endif // QTIMEROBJECTINQTHREAD_H
- 实现
QTimerObjectInQThread
类
#include "QTimerObjectInQThread.h"
#include <QDebug>
QTimerObjectInQThread::QTimerObjectInQThread()
: m_count(0)
{
}
QTimerObjectInQThread::~QTimerObjectInQThread()
{
if(m_ptimer)
{
m_ptimer->stop();
delete m_ptimer;
}
}
void QTimerObjectInQThread::timeoutHandle()
{
qDebug() << "current thread id: " << QThread::currentThreadId() << "m_count: " << m_count++;
}
void QTimerObjectInQThread::run()
{
m_ptimer = new QTimer;
connect(m_ptimer,&QTimer::timeout,this,&QTimerObjectInQThread::timeoutHandle);
m_ptimer->start(100);
// 启动事件循环,否则定时器无法生效
exec();
}
- main.cpp
#include <QCoreApplication>
#include "QTimerRunInThread.h"
#include "QTimerMoveToChildrenThread.h"
#include "QTimerObjectInQThread.h"
#include <QDebug>
#include <QThread>
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
qDebug() << "main thread id: " << QThread::currentThreadId() ;
/*
* 第二种方式:定时器运行在子线程中
* 在线程的run()中启动定时器
*/
QTimerObjectInQThread timerObjectInThread;
timerObjectInThread.start();
return a.exec();
}
2.3.2.1 定时器不能在另一个线程启动问题演示
- 将
QTimer* m_ptimer;
修改为QTimer m_ptimer;
QTimerObjectInQThread
类的实现修改如下#include "QTimerObjectInQThread.h" #include <QDebug> QTimerObjectInQThread::QTimerObjectInQThread() : m_count(0) { } QTimerObjectInQThread::~QTimerObjectInQThread() { // if(m_ptimer) // { // m_ptimer->stop(); // delete m_ptimer; // } } void QTimerObjectInQThread::timeoutHandle() { qDebug() << "current thread id: " << QThread::currentThreadId() << "m_count: " << m_count++; } void QTimerObjectInQThread::run() { //m_ptimer = new QTimer; connect(&m_ptimer,&QTimer::timeout,this,&QTimerObjectInQThread::timeoutHandle); m_ptimer.start(100); // 启动事件循环,否则定时器无法生效 exec(); }
- 运行项目会输出以下内容
QObject::startTimer: Timers cannot be started from another thread