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已经做的之外,不需要额外跟踪定时器。
答案 0 :(得分:2)
一个简单的解决方案是防止问题:如果对象即将变为无线程,则将其移动到线程句柄的父线程,然后当线程本身即将被破坏时,重新建立对象的计时器以防止警告
QObject
的{{1}}实施包含两个部分:
moveToThread
从QEvent::ThreadChange
传递到对象。 moveToThread
使用此事件来捕获和停用对象上活动的计时器。这些计时器打包在一个列表中,并发布到对象的内部QObject::event
方法。
目标线程中的事件循环将metacall传递给对象,_q_reregisterTimers
在新线程中运行,计时器在新线程中重新激活。请注意,如果_q_reregisterTimers
无法运行,将不可避免地泄漏计时器列表。
因此我们需要:
捕获对象即将变为无线程的时刻,并将其移动到另一个线程,以便_q_reactivateTimers
到QMetaCallEvent
不会丢失。
以正确的帖子发送活动。
所以:
_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();
}