如何防止QBasicTimer :: stop:当对象变为无线程时失败警告?

时间:2018-06-01 04:24:11

标签: qt qthread

当他们的工作线程在他们之前完成时,

QObject很容易变成无线程。发生这种情况时,即使定时器不再处于活动状态,Qt也不会释放它们的定时器ID。因此,出现QBasicTimer::stop: Failed. Possibly trying to stop from a different thread警告。它主要是外观上的后果,但确实表明计时器ID泄漏,因此一个变通方法会很好。以下示例触发了此问题:

#include <QtCore>
int main(int argc, char *argv[]) {
   static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), "");
   QCoreApplication app(argc, argv);
   QObject object;
   object.startTimer(1000);
   QThread workThread;
   workThread.start();
   object.moveToThread(&workThread);
   QTimer::singleShot(500, &QCoreApplication::quit);
   app.exec();
   workThread.quit();
   workThread.wait();
}

如果解决方法不必对定时器的分配方式进行任何修改,那将是很好的,即除了Qt已经做的之外,不需要额外跟踪定时器。

3 个答案:

答案 0 :(得分:2)

一个简单的解决方案是防止问题:如果对象即将变为无线程,则将其移动到线程句柄的父线程,然后当线程本身即将被破坏时,重新建立对象的计时器以防止警告

QObject的{​​{1}}实施包含两个部分:

  1. moveToThreadQEvent::ThreadChange传递到对象。 moveToThread使用此事件来捕获和停用对象上活动的计时器。这些计时器打包在一个列表中,并发布到对象的内部QObject::event方法。

  2. 目标线程中的事件循环将metacall传递给对象,_q_reregisterTimers在新线程中运行,计时器在新线程中重新激活。请注意,如果_q_reregisterTimers无法运行,将不可避免地泄漏计时器列表

  3. 因此我们需要:

    1. 捕获对象即将变为无线程的时刻,并将其移动到另一个线程,以便_q_reactivateTimersQMetaCallEvent不会丢失。

      < / LI>
    2. 以正确的帖子发送活动。

    3. 所以:

      _q_reactivateTimers

      这种方法可以轻松扩展,以便动态跟踪// https://github.com/KubaO/stackoverflown/tree/master/questions/qbasictimer-stop-fix-50636079 #include <QtCore> class Thread final : public QThread { Q_OBJECT void run() override { connect(QAbstractEventDispatcher::instance(this), &QAbstractEventDispatcher::aboutToBlock, this, &Thread::aboutToBlock); QThread::run(); } QAtomicInt inDestructor; public: using QThread::QThread; /// Take an object and prevent timer resource leaks when the object is about /// to become threadless. void takeObject(QObject *obj) { // Work around to prevent // QBasicTimer::stop: Failed. Possibly trying to stop from a different thread static constexpr char kRegistered[] = "__ThreadRegistered"; static constexpr char kMoved[] = "__Moved"; if (!obj->property(kRegistered).isValid()) { QObject::connect(this, &Thread::finished, obj, [this, obj]{ if (!inDestructor.load() || obj->thread() != this) return; // The object is about to become threadless Q_ASSERT(obj->thread() == QThread::currentThread()); obj->setProperty(kMoved, true); obj->moveToThread(this->thread()); }, Qt::DirectConnection); QObject::connect(this, &QObject::destroyed, obj, [obj]{ if (!obj->thread()) { obj->moveToThread(QThread::currentThread()); obj->setProperty(kRegistered, {}); } else if (obj->thread() == QThread::currentThread() && obj->property(kMoved).isValid()) { obj->setProperty(kMoved, {}); QCoreApplication::sendPostedEvents(obj, QEvent::MetaCall); } else if (obj->thread()->eventDispatcher()) QTimer::singleShot(0, obj, [obj]{ obj->setProperty(kRegistered, {}); }); }, Qt::DirectConnection); obj->setProperty(kRegistered, true); } obj->moveToThread(this); } ~Thread() override { inDestructor.store(1); requestInterruption(); quit(); wait(); } Q_SIGNAL void aboutToBlock(); }; int main(int argc, char *argv[]) { static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), ""); QCoreApplication app(argc, argv); QObject object1, object2; object1.startTimer(10); object2.startTimer(200); Thread workThread1, workThread2; QTimer::singleShot(500, &QCoreApplication::quit); workThread1.start(); workThread2.start(); workThread1.takeObject(&object1); workThread2.takeObject(&object2); app.exec(); } #include "main.moc" 的所有子项:Qt提供了足够的事件来进行此类跟踪。

答案 1 :(得分:-1)

保持计时器ID在线程内被杀死 - object

 int id = object.startTimer(1000);
 QThread workThread;
 workThread.start();
 object.moveToThread(&workThread);
 QTimer::singleShot(500, &QCoreApplication::quit);
 QObject::connect(&workThread, &QThread::finished, [&](){object.killTimer(id);});

...

答案 2 :(得分:-1)

如何将对象移回主线程......

class Object : public QObject
{
public:
    using QObject::QObject;
    virtual ~Object() {
        qDebug()<<"Object"<<QThread::currentThread()<<this->thread();
        if(thread() == Q_NULLPTR)
            moveToThread(QThread::currentThread());
    }
};

#include <QtCore>
int main(int argc, char *argv[]) {
   static_assert(QT_VERSION < QT_VERSION_CHECK(5,11,0), "");
   QCoreApplication app(argc, argv);
   Object object;
   object.startTimer(1000);
   QThread workThread;
   workThread.start();
   object.moveToThread(&workThread);
   QTimer::singleShot(500, &QCoreApplication::quit);
   qDebug()<<"main"<<QThread::currentThread()<<object.thread();
   app.exec();
   workThread.quit();
   workThread.wait();
}