何时或如何在Qt中删除QThread

6

我在这里找到了一个QThread的例子。它建议使用QObject和QThread而不是子类化QThread。

class Worker : public QObject
{
   Q_OBJECT

public slots:
    void doWork() {
        /* ... */
    }
};

QThread *thread = new QThread;
Worker *worker = new Worker;
worker->moveToThread(thread);
thread->start();
QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

我的第一个问题是何时以及如何删除线程?

我尝试将finished连接到两个插槽,myTest和deleteLater。我在myTest中设置了断点,但它从未被触发。因此,我怀疑没有finished信号,这意味着线程不会被删除。

connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), this, SLOT(myTest()));

对于 worker 对象,我在 doWork 的最后一条指令中发出 finished 信号,并将其连接到一个槽中,在该槽中可以删除 worker 对象。这样做是正确的吗?

Qt 版本:4.6.2


2
可能是如何释放QThread对象的内存?的重复问题。 - Iuliu
先将线程的 finished() 信号连接到工作线程,然后再连接到线程自己的 deleteLater() 插槽。 - dtech
3
否则线程永远不会发出“finished”信号,因此必须有某个东西来告诉线程“退出”。 - thuga
哎呀,看起来我的最后一条评论只适用于4.8版本之后的Qt,很可能在4.6.2中无法正常工作。 - dtech
1
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Shf
谢谢大家。假设我可以使线程的生命周期与主窗口相同,但是出于学习目的,如果我想提前完成线程,我应该手动调用 thread->exit() 并将其重新分配为 null 吗?或者像 @Shf 所说的那样,在连接 finished 到 deleteLater 之前连接信号到 quit 槽? - Jerry
2个回答

10

当线程的 worker 方法执行完成时,它会正常退出,但如果您想在线程退出时做一些事情,请使用 finished() 信号。通过将 finished() 信号连接到 QObject::deleteLater(),可以释放刚刚结束的线程中存在的对象:

connect(workerThread, &WorkerThread::finished, workerThread, &QObject::deleteLater);

0

使用connect()语句删除后面的thread的方法应该是可行的。

您建议的在worker完成后删除的方法也应该可行。

但是,为了触发您的槽myTest(),您需要添加更多代码:

  1. worker完成时,使用connect()语句调用thread->quit()worker->deleteLater()this->deleteLater()
  2. 在上述connect()语句中使用Qt::DirectConnection

以下是一个测试示例,实现上述解决方案,假设您的类名为Tester

文件worker.h

#ifndef WORKER_H
#define WORKER_H

#include <QObject>

class Worker : public QObject
{
Q_OBJECT
public:
    Worker();
    ~Worker();
signals:
    void finished();
public slots:
    void doWork();
};

#endif // WORKER_H

文件 worker.cpp

#include "worker.h"

#include <QDebug>

Worker::Worker()
{
    qDebug() << "D/Worker==Worker";
}

Worker::~Worker()
{
    qDebug() << "D/Worker==~Worker";
}

void Worker::doWork()
{
    qDebug() << "D/Worker==doWork";

    // Do work here

    emit finished();
}

文件 tester.h:

#ifndef TESTER_H
#define TESTER_H

#include <QObject>

class Tester : public QObject
{
    Q_OBJECT
public:
    Tester();
    ~Tester();
public:
    void startTesting();
public slots:
    void myTest();
};

#endif // TESTER_H

文件 tester.cpp

#include "tester.h"

#include "worker.h"

#include <QThread>
#include <QDebug>

Tester::Tester()
{
    qDebug() << "D/Tester==Tester";
}

Tester::~Tester()
{
    qDebug() << "D/Tester==~Tester";
}

void Tester::startTesting()
{
    qDebug() << "D/Tester==startTesting";

    QThread * thread = new QThread;
    Worker * worker = new Worker;
    worker->moveToThread(thread);
    connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()), Qt::DirectConnection);
    connect(thread, SIGNAL(finished()), this,   SLOT(myTest()),      Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), thread, SLOT(quit()),        Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()), Qt::DirectConnection);
    connect(worker, SIGNAL(finished()), this,   SLOT(deleteLater()), Qt::DirectConnection);
    thread->start();
    QMetaObject::invokeMethod(worker, "doWork", Qt::QueuedConnection);

    // "thread" is deleted later.
    // "worker" is deleted later.
}

void Tester::myTest()
{
    qDebug() << "D/Tester==myTest";
}

文件 main.cpp

#include <QCoreApplication>
#include <QObject>
#include <QDebug>

#include "worker.h"
#include "tester.h"

int main(int argc, char *argv[])
{
    qDebug() << "D/TestQThreadNewDelete==main";

    QCoreApplication a(argc, argv);

    Tester * tester = new Tester;

    tester->startTesting();

    // "tester" is deleted later in tester->onWorkerFinished().

    return a.exec();
}

在Linux上使用Qt 5.5.0运行此测试应用程序的输出如下:
D/TestQThreadNewDelete==main
D/Tester==Tester
D/Tester==startTesting
D/Worker==Worker
D/Worker==doWork
D/Tester==myTest
D/Worker==~Worker
D/Tester==~Tester

从中我们可以看到:

  1. 槽函数 Tester::myTest() 被触发。
  2. 因此,信号 QThread::finished() 应该已经被发射了。
  3. 同样地,槽函数 QThread::deleteLater() 也会被触发。这可能发生在测试应用程序退出时。但是,需要进行调试以确认这一点,我还没有尝试过。

最后一次使用Qt :: DirectConnection连接是错误的,会导致您的应用程序崩溃,因为Tester将在子线程中被销毁。不要在DirectConnection和线程之间搞混。 - galinette
你是对的。我犯了错误,没有检查每一行。所以在最后一个connect语句中不要使用Qt::DirectConnection。谢谢你指出这一点。 - jonathanzh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接