我想正确地破坏一个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();
}
它会做出相应的表现。
QObject的成员并不需要在堆中
另一个问题是, Worker
对象会被泄露。 而不是把所有这些对象在堆上的,只是让他们的成员MyClass
或其PIMPL。
成员声明的顺序是重要的 ,因为成员将在声明的相反的顺序破坏。 因此,析构函数MyClass
会,调用,依次是:
m_workerThread.~Thread()
此时线程完成,并走了, m_worker.thread() == 0
。
m_worker.~Worker
由于对象是无螺纹,它的安全,摧毁它的任何线索。
~QObject
因此,与工人和其作为成员线程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
成员到另一个线程。 成员声明的顺序必须是这样的线程是对象之前被破坏 。 如果是无效的破坏驻留在另一个线程的对象。
它是唯一安全的破坏一个QObject
,如果以下断言成立:
Q_ASSERT(!object->thread() || object->thread() == QThread::currentThread())
一个对象的线程被破坏成为螺纹,和!object->thread()
持有。
可能有人会说我们不“打算”我们班被移动到另一个线程。 如果是这样,那么很明显,我们的对象不是QObject
了,因为QObject
有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();
}
mThread-> requestInterruption()不停止线程瞬间,它只是一个方式来显示你正在运行的代码,以完全结束(你必须检查isInterruptionRequested(),并停止计算自己)。
从Qt的文档:
请求的线程中断。 该请求咨询和它是由线程上运行的代码,以决定它应该是否以及如何在这样的请求采取行动。 此功能不会对任何事件循环的线程上运行,并以任何方式不终止它。 另请参见isInterruptionRequested()。