我想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();
}
Run Code Online (Sandbox Code Playgroud)
我的问题是,在一天结束时,我仍然得到:
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();
}
Run Code Online (Sandbox Code Playgroud)
它会表现得恰到好处.
另一个问题是该Worker物体将被泄露.而不是将所有这些对象放在堆上,只需将它们作为成员MyClass或其PIMPL.
成员声明的顺序很重要,因为成员将按照与声明相反的顺序进行破坏.因此,MyClasswill 的析构函数按顺序调用:
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();
}
};
Run Code Online (Sandbox Code Playgroud)
并且,如果您不希望实现在接口中,则使用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()
{}
Run Code Online (Sandbox Code Playgroud)
我们现在看到一个关于任何QObject成员的父母身份的硬性规则.只有两种情况:
如果QObject成员未从类中移动到另一个线程,则它必须是该类的后代.
否则,我们必须将QObject成员移动到另一个线程.成员声明的顺序必须使得线程在对象之前被销毁.如果无效,则破坏驻留在另一个线程中的对象.
QObject如果以下断言成立,则破坏a是安全的:
Q_ASSERT(!object->thread() || object->thread() == QThread::currentThread())
Run Code Online (Sandbox Code Playgroud)
线程被破坏的对象变为无线,并!object->thread()保持.
有人可能会争辩说,我们并没有"打算"将我们的类转移到另一个线程.如果是这样,那么显然我们的对象QObject不再是,因为a 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();
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
2355 次 |
| 最近记录: |