QTimer启动出错: QObject::startTimer: Timers can only be used with threads started with QThread

目录

1.现象

2.原因分析

3.解决方法

3.1.在QT主线程(GUI线程)中使用QTimer

3.2.在非GUI线程中使用QTimer

4.总结


1.现象

        目前项目上需要使用定时器,首先想到的是用QT自带的定时器QTimer;项目实现如下:

        首先在头文件中定义一个QTimer,代码如下:

#pragma once
#include <QTimer>

class CTTNTAppUpCmdPro : public IShortWaveAppUpCmdPro
{
public:
	explicit CTTNTAppUpCmdPro(quint64 chaissID, quint64 signalID, void* p, IUploadDataBase* pInterface);
	virtual ~CTTNTAppUpCmdPro();

private:
    void  _dealControlDeviceResponse(const void* p, int source);

private:
	void startPollDataTimer();
	void endPollDataTimer();

private:
	QTimer m_pollDataTimer;
};

        接下来实现QTimer的信号timeOut和定时事件的关联,启动定时器,代码如下:

void CTTNTAppUpCmdPro::startPollDataTimer()
{
	m_pollDataTimer.setInterval(1000);  //1秒5次
	QObject::connect(&m_pollDataTimer, &QTimer::timeout, [&]() {
		sendQueryDataCmd();
		});
	m_pollDataTimer.start();
}

void CTTNTAppUpCmdPro::endPollDataTimer()
{
	m_pollDataTimer.stop();
}

//设备启动停止应答
void  CTTNTAppUpCmdPro::_dealControlDeviceResponse(const void* p, int source)
{
	const stShortWavePacket* pAppPacket = static_cast<const stShortWavePacket*>(p);
	assert(pAppPacket);
	const stLinkDeviceCmdResponse* pCommData = static_cast<const stLinkDeviceCmdResponse*>(pAppPacket->pAppData);
	assert(pCommData);

	//...

	if (pCommData->cmdType == 1 && st.IsSuccess) { //启动成功
		startPollDataTimer(); //启动定时查询TTNT数据线程
	}
	if (pCommData->cmdType == 2 /*&& st.IsSuccess*/) { //停止成功
		endPollDataTimer();  //关闭定时查询TTNT数据线程
	}
}

经过调试发现,在调用_dealControlDeviceResponse函数的启动定时器startPollDataTimer的时候,在控制台上就会显示:

 QObject::startTimer: Timers can only be used with threads started with QThread

2.原因分析

        翻开QTimer的源码实现(5.12.12版本)可以看到:

出现QObject::startTimer: Timers can only be used with threads started with QThread的原因是当前对象所属的线程对象的eventDisptcher是nullptr,为什么会出现这样的情况呢?那是因为当前线程不是从QThread派生出来的,QObject::startTimer 方法依赖于Qt的事件循环(event loop)来触发定时器事件。每个QObject对象都与一个特定的线程相关联,这个线程必须是Qt创建并管理的线程。如果你在一个非Qt线程中使用startTimer,定时器将无法在Qt的事件循环中注册,因此它不会触发任何事件。

3.解决方法

3.1.在QT主线程(GUI线程)中使用QTimer

        因为主线程默认是由 Qt 框架管理的,并且是基于 QThread 的。但是,如果你在主线程中创建了一个新的线程,并且在这个新线程中直接使用了 QObject::startTimer 而没有正确地管理这个线程(例如,没有将其作为一个 QThread 对象来启动),就可能会遇到这个错误。

        通过调试第1章节的代码发现QTimer的定义和它的startTimer不是在同一个线程中,QTimer的定义既非在主线程中也非在基于QThread的线程中,所以会出现报错;但同时发现startTimer的线程是在主线程中的,所以稍加修改:

#pragma once
#include <QTimer>
#include <memory>

class CTTNTAppUpCmdPro : public IShortWaveAppUpCmdPro
{
public:
	explicit CTTNTAppUpCmdPro(quint64 chaissID, quint64 signalID, void* p, IUploadDataBase* pInterface);
	virtual ~CTTNTAppUpCmdPro();

private:
    void  _dealControlDeviceResponse(const void* p, int source);

private:
	void startPollDataTimer();
	void endPollDataTimer();

private:
	std::unique_ptr<QTimer> m_pollDataTimer;
};
void CTTNTAppUpCmdPro::startPollDataTimer()
{
    m_pollDataTimer.reset(new QTimer(null))
	m_pollDataTimer->setInterval(1000);  //1秒5次
	QObject::connect(m_pollDataTimer.get(), &QTimer::timeout, [&]() {
		sendQueryDataCmd();
		});
	m_pollDataTimer->start();
}

void CTTNTAppUpCmdPro::endPollDataTimer()
{
	m_pollDataTimer->stop();
}

编译运行,果然就能正常触发定时任务了。

3.2.在非GUI线程中使用QTimer

        如果你创建了一个继承自 QThread 的类,并在这个类的 run() 方法中直接使用 QObject::startTimer,这通常是可以的,前提是确保这个 QThread 对象已经被正确地启动(即调用了 start() 方法)。然而,如果你在一个普通的 C++ 线程(例如使用 std::thread)中尝试使用 QObject::startTimer,就会触发这个错误,因为 QObject 的定时器机制依赖于 Qt 的事件循环,而 std::thread 并不提供这样的事件循环。

        示例代码如下:

#include <QThread>
#include <QTimer>
#include <QObject>
 
class Worker : public QObject {
    Q_OBJECT
public:
    void doWork() {
        QTimer *timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &Worker::onTimeout);
        timer->start(1000); // 每秒触发一次
    }
 
private slots:
    void onTimeout() {
        // 处理定时器事件
        qDebug() << "Timer triggered";
    }
};
 
class Thread : public QThread {
protected:
    void run() override {
        Worker worker;
        worker.doWork(); // 在这个线程中启动定时器
        exec(); // 启动Qt的事件循环
    }
};

4.总结

  • 确保你在一个由 QThread 启动的线程中使用 QObject::startTimer
  • 如果你需要在非GUI线程中使用定时器,确保这个线程是通过继承 QThread 并重写 run() 方法来创建的,并且在启动线程时调用了 start() 方法。
  • 避免在不是由 QThread 管理的线程中使用 Qt 的信号和槽机制或任何依赖于事件循环的功能。

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值