我想QThread
在Qt 5.3中正确销毁一个。
到目前为止,我已经:
MyClass::MyClass(QObject *parent) : QObject(parent) {
mThread = new QThread(this);
QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater()));
mWorker = new Worker(); // inherits from QObject
mWorker->moveToThread(mThread);
mThread->start();
}
MyClass::~MyClass() {
mThread->requestInterruption();
}
我的问题是,到最后我仍然得到:
QThread:线程仍在运行时被销毁
在C ++中,对类的适当设计应确保可以随时安全地销毁实例。几乎所有的Qt类都以这种方式起作用,但QThread
事实并非如此。
这是您应该使用的类:
// Thread.hpp
#include <QThread>
public Thread : class QThread {
Q_OBJECT
using QThread::run; // This is a final class
public:
Thread(QObject * parent = 0);
~Thread();
}
// Thread.cpp
#include "Thread.h"
Thread::Thread(QObject * parent): QThread(parent)
{}
Thread::~Thread() {
quit();
#if QT_VERSION >= QT_VERSION_CHECK(5,2,0)
requestInterruption();
#endif
wait();
}
它将表现适当。
另一个问题是Worker
对象将泄漏。不必将所有这些对象放在堆上,只需使它们MyClass
成为PIMPL的成员即可。
成员声明的顺序很重要,因为将按照相反的声明顺序破坏成员。因此,MyClass
will的析构函数按以下顺序调用:
m_workerThread.~Thread()
至此,线程结束并消失了,并且m_worker.thread() == 0
。
m_worker.~Worker
由于对象是无线程的,因此可以安全地在任何线程中销毁它。
~QObject
因此,使用worker及其线程作为以下成员MyClass
:
class MyClass : public QObject {
Q_OBJECT
Worker m_worker; // **NOT** a pointer to Worker!
Thread m_workerThread; // **NOT** a pointer to Thread!
public:
MyClass(QObject *parent = 0) : QObject(parent),
// The m_worker **can't** have a parent since we move it to another thread.
// The m_workerThread **must** have a parent. MyClass can be moved to another
// thread at any time.
m_workerThread(this)
{
m_worker.moveToThread(&m_workerThread);
m_workerThread.start();
}
};
而且,如果您不希望实现在接口中,则使用PIMPL相同
// MyClass.hpp
#include <QObject>
class MyClassPrivate;
class MyClass : public QObject {
Q_OBJECT
Q_DECLARE_PRIVATE(MyClass)
QScopedPointer<MyClass> const d_ptr;
public:
MyClass(QObject * parent = 0);
~MyClass(); // required!
}
// MyClass.cpp
#include "MyClass.h"
#include "Thread.h"
class MyClassPrivate {
public:
Worker worker; // **NOT** a pointer to Worker!
Thread workerThread; // **NOT** a pointer to Thread!
MyClassPrivate(QObject * parent);
};
MyClassPrivate(QObject * parent) :
// The worker **can't** have a parent since we move it to another thread.
// The workerThread **must** have a parent. MyClass can be moved to another
// thread at any time.
workerThread(parent)
{}
MyClass::MyClass(QObject * parent) : QObject(parent),
d_ptr(new MyClassPrivate(this))
{
Q_D(MyClass);
d->worker.moveToThread(&d->workerThread);
d->workerThread.start();
}
MyClass::~MyClass()
{}
我们现在看到关于任何QObject
成员的亲戚关系都出现了硬性规定。只有两种情况:
如果QObject
成员没有从类中移到另一个线程,则它必须是该类的后代。
否则,我们必须将QObject
成员移动到另一个线程。成员声明的顺序必须这样,以便在对象之前销毁线程。如果无效,则销毁驻留在另一个线程中的对象。
仅QObject
当以下断言成立时,才可以销毁a :
Q_ASSERT(!object->thread() || object->thread() == QThread::currentThread())
线程被破坏的对象变为无线程,并!object->thread()
保持。
有人可能会争辩说我们不“打算”将我们的班级移到另一个线程。如果是这样,那么显然我们的对象已QObject
不再存在,因为aQObject
具有moveToThread
方法并且可以随时移动。如果某个类不遵循Liskov对其基类的替代原则,则要求从该基类获得公共继承是错误的。因此,如果我们的类公开地继承自QObject
,则它必须允许其随时移动到任何其他线程。
的QWidget
是一个位在这方面的一个异常值的。至少,它应该已经成为moveToThread
一种受保护的方法。
例如:
class Worker : public QObject {
Q_OBJECT
QTimer m_timer;
QList<QFile*> m_files;
...
public:
Worker(QObject * parent = 0);
Q_SLOT bool processFile(const QString &);
};
Worker::Worker(QObject * parent) : QObject(parent),
m_timer(this) // the timer is our child
// If m_timer wasn't our child, `worker.moveToThread` after construction
// would cause the timer to fail.
{}
bool Worker::processFile(const QString & fn) {
QScopedPointer<QFile> file(new QFile(fn, this));
// If the file wasn't our child, `moveToThread` after `processFile` would
// cause the file to "fail".
if (! file->open(QIODevice::ReadOnly)) return false;
m_files << file.take();
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句