如何安全地破坏一个QThread的?如何安全地破坏一个QThread的?(How to safely

2019-05-11 20:45发布

我想正确地破坏一个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的:被毁坏,而线程仍在运行

Answer 1:

安全主题

在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会,调用,依次是:

  1. m_workerThread.~Thread()此时线程完成,并走了, m_worker.thread() == 0

  2. m_worker.~Worker由于对象是无螺纹,它的安全,摧毁它的任何线索。

  3. ~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成员。 只有两种情况:

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

  2. 否则,我们必须将移动QObject成员到另一个线程。 成员声明的顺序必须是这样的线程是对象之前破坏 。 如果是无效的破坏驻留在另一个线程的对象。

它是唯一安全的破坏一个QObject ,如果以下断言成立:

Q_ASSERT(!object->thread() || object->thread() == QThread::currentThread())

一个对象的线程被破坏成为螺纹,和!object->thread()持有。

可能有人会说我们不“打算”我们班被移动到另一个线程。 如果是这样,那么很明显,我们的对象不是QObject了,因为QObjectmoveToThread方法,可以随时移动。 如果一个类不服从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();
}


Answer 2:

mThread-> requestInterruption()不停止线程瞬间,它只是一个方式来显示你正在运行的代码,以完全结束(你必须检查isInterruptionRequested(),并停止计算自己)。

从Qt的文档:

请求的线程中断。 该请求咨询和它是由线程上运行的代码,以决定它应该是否以及如何在这样的请求采取行动。 此功能不会对任何事件循环的线程上运行,并以任何方式不终止它。 另请参见isInterruptionRequested()。



文章来源: How to safely destruct a QThread?
标签: c++ qt qthread