QTimer没有在线程中触发

Posted

技术标签:

【中文标题】QTimer没有在线程中触发【英文标题】:QTimer not firing in a thread 【发布时间】:2014-08-10 00:42:57 【问题描述】:

我有一个带有 2 个线程的 Qt5 c++ 应用程序,线程 A 在主程序启动时启动。线程A的start方法运行成功。

到目前为止一切顺利。接下来,在主程序中,我向线程 A 发送一个信号以启动一个 QTimer,它确实如此 - 但该定时器永不过期!

线程 B 处理 tcp 连接。当我启动到我的应用程序的 telnet 连接时,线程 B 启动,突然我看到线程 A 的 Qtimer 以正常间隔到期。

为什么线程 A 的 QTimer 直到线程 B 启动才过期?

我怀疑我的线程搞砸了。请注意产品下面的最后一段代码:

thread of this:  QThread(0x200fe00)  
thread of timer:  QThread(0x1fff470)

这表明我的工作对象(this)与我的计时器对象位于不同的线程中。这个定时器线程地址其实就是MAIN线程。为什么?我很困惑。

建议?


在我的主应用程序中,我像这样创建并启动我的线程:

QThread * MyControllerThread = new QThread(this);

if (MyControllerThread) 

    TheController *worker = new TheController(MyControllerThread);

    if (worker) 
        connect(MyControllerThread, SIGNAL(started()), worker, SLOT(start()));
        connect(MyControllerThread, SIGNAL(finished()), worker, SLOT(deleteLater()));
        connect(MyControllerThread, SIGNAL(finished()), MyControllerThread, SLOT(deleteLater()));
        worker->moveToThread(MyControllerThread);  
        MyControllerThread->start();  
     

在我的主应用程序中,我向新线程发出信号:

    emit sig_startlocalpeer(Types::EActionLocalServiceStart);  // Move the local peer to standby mode to start remote tests

在我的线程中运行一个插槽(TheController 对象):

connect(&m_remotetestintervaltimer,SIGNAL(timeout()),this,SLOT(expiredRemoteTestIntervalTimer()));
m_remotetestintervaltimer.setTimerType(Qt::VeryCoarseTimer);
m_remotetestintervaltimer.start(REMOTETEST_TIMER_INTERVAL);  // Wait between ticks
qDebug() << "thread of this: " << this->thread();
qDebug() << "thread of timer: " << m_remotetestintervaltimer.thread();

【问题讨论】:

我已经调用了线程的start()方法。我从哪里调用 run()?我需要两个都打电话吗? 能否请您发布一个小代码示例来说明您的问题? 因为我创建了 2 个线程,一个主应用程序,并且计时器是在一个插槽中启动的,所以有很多代码要发布。如果没有发布简单的 A-HA 答案,我会发布...但希望避免发布非常大的帖子 抱歉,start() 是正确的调用方法。 您没有发布足够多的代码来说明问题所在 - 正如您在 cmets 中明确指出的答案之一。这种不完整的问题对其他人没有用处,并且在这里是题外话。我真的强调在问题中发布SSCCE 的重要性。复制、粘贴、(编译、)看是目的。否则,我们会浪费时间向您询问其他不需要的问题,并且需要更长的时间才能得到答案。因此,您浪费了我们的时间和您自己的时间。这显然适得其反。 【参考方案1】:

嗯,这不是Qt5的bug,更多的是对Qt的线程精神的理解不准确。

在 Qt 中,您有两种方法来实现线程,它们使用或不使用偶数循环。这只是一个小的视觉示例。

无事件循环

myMethodCalledInANewThread

    do ... while(...);

带有事件循环

myMethodCalledInANewThread

    [...]
    exec();

(当然,您可以将 do/while 与偶数循环混合使用,但要保持简单)。

在 QTimer 的文档中,您可以阅读:

在多线程应用程序中,您可以在任何线程中使用 QTimer 有一个事件循环。 [...] Qt 使用计时器的线程亲和性来 确定哪个线程将发出 timeout() 信号。因为 这样,您必须在其线程中启动和停止计时器;它不是 可以从另一个线程启动计时器。

所以我很确定您的第二个线程中没有第二个事件循环,这就是您所描述的行为的原因。

为了给你一些使用 Qt 线程的技巧,我建议你阅读:

QThread 文档:https://doc.qt.io/qt-5/qthread.html QTimer 文档:https://doc.qt.io/qt-5/qtimer.html

还有一篇关于 QThread 实现如何被很多用户误解的非常好的文章:

你做错了:https://www.qt.io/blog/2010/06/17/youre-doing-it-wrong

我希望它会有所帮助;)

【讨论】:

我阅读了这两个文档,但看起来我做的一切都是正确的。计时器在新线程 A 中启动/停止。但是,Qtimer 在主线程的一部分被实例化(未启动)并移动到线程 A(然后启动)。 从 Qt 4.8 开始,您似乎也不再需要在线程中调用 exec 了。启动槽会自动为线程启动一个事件循环。 嗯,好吧,如果没有看到足够的代码,很难帮助你(正如上面的 kuba 所说!)。你能上传一个简单的项目来重现你的问题吗?谢谢你:)【参考方案2】:

最好的答案似乎是 RobbieE 和 Kuba 的组合:

您必须在构造函数中显式设置成员变量的父级。父子特性是 Qt 的东西,存在于从 QObject 派生的类中,它不是 C++ 的特性。

我从来不知道这一点——我假设当一个对象被创建时,它的成员变量会自动将它们的父级设置为该对象。很高兴知道!

【讨论】:

您的 QTimer 对象是无父对象还是工人类是其父对象?当您将 Qbject 移动到另一个线程时,只有它自己和子对象被移动到新线程。移动之前在主线程中创建的所有其他成员对象保持与主线程对齐。 有趣的想法... QTimer 是我的工人阶级的成员变量。所以它的父对象应该是工作者对象。当工作对象被移动时,QTimer 应该随之移动。听起来对吗? "所以它的父对象应该是工作对象。"不。要理解这一点,请解释对象实例如何知道它在哪里实例化(局部变量、实例成员、类成员、堆)。当然不能,C++ 中没有这样的规定。除了让计时器成为 worker 对象的成员之外,您还必须将它 (this) 作为父级传递给 worker 的构造函数:Worker::Worker(QObject*parent) : m_timer(this) ... 当我实例化类时,我得到一个带有成员变量的对象(包括 QTimer)。如果我将对象移动到新线程,成员变量不应该随之移动吗? @GenerationDSystems 否。只有将您要移动到新线程的对象设置为其父级的对象,即其子级。

以上是关于QTimer没有在线程中触发的主要内容,如果未能解决你的问题,请参考以下文章

python 多线程 QTimer实现多线程

Pyqt5 Qtimer理解

在 QThread 中启动 QTimer

pyqt4:在Qthread中使用定时器Qtimer注意

如何在 QThread 中使用 QTimer?

QTimer 线程亲和性