如何安全地销毁QThread?

尼克拉斯

我想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的成员即可

成员声明的顺序很重要,因为将按照相反的声明顺序破坏成员因此,MyClasswill的析构函数按以下顺序调用:

  1. m_workerThread.~Thread()至此,线程结束并消失了,并且m_worker.thread() == 0

  2. m_worker.~Worker 由于对象是无线程的,因此可以安全地在任何线程中销毁它。

  3. ~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成员的亲戚关系都出现了硬性规定只有两种情况:

  1. 如果QObject成员没有从类中移到另一个线程,则它必须是该类的后代

  2. 否则,我们必须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] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何安全地销毁CD / DVD ROM?

来自分类Dev

如何使用智能指针安全地销毁不完整对象类型的类?

来自分类Dev

如何安全地扩展Ubuntu分区?

来自分类Dev

如何安全地发布StringBuffer?

来自分类Dev

如何快速安全地存储密码?

来自分类Dev

如何安全地搬运包裹intellij?

来自分类Dev

如何安全地重构动态语言?

来自分类Dev

如何安全地登录用户

来自分类Dev

如何安全地从终端关闭Xfce?

来自分类Dev

您如何安全地调用vsnprintf()?

来自分类Dev

如何安全地试用叉子炸弹

来自分类Dev

如何安全地等待异步方法?

来自分类Dev

如何安全地从终端关闭Xfce?

来自分类Dev

如何安全地扩展Ubuntu分区?

来自分类Dev

如何安全地更新网站?

来自分类Dev

如何安全地共享班级成员?

来自分类Dev

如何安全地登录用户

来自分类Dev

如何安全地取出包裹

来自分类Dev

如何安全地减小分区的大小?

来自分类Dev

如何安全地停止截断命令

来自分类Dev

如何安全地存储各种凭证?

来自分类Dev

如何安全地移除 gwibber?

来自分类Dev

如何“安全地”终止mysqld?

来自分类Dev

如何安全地重置 Oracle 序列?

来自分类Dev

如何安全地改变指针的类型

来自分类Dev

如何安全地解开textField.text并将其安全地存储在属性中?

来自分类Dev

如何安全地从Ruby Hash解析数据?

来自分类Dev

如何使用GCD安全地锁定变量?

来自分类Dev

如何安全地向REST服务发送密码?