【问题标题】:Qt thread does not stop after calling exit/quitQt 线程在调用 exit/quit 后不会停止
【发布时间】:2012-02-20 08:26:02
【问题描述】:

我试图更好地理解 Qt 信号和插槽与线程的结合。所以我尝试了这个最小的应用程序:

foo.h:

#include <QObject>

class A : public QObject {
  Q_OBJECT

public:
  void doit();

signals:
  void x();
};

class B : public QObject {
  Q_OBJECT

public slots:
  void h();
};

foo.cpp:

#include "foo.h"

#include <QThread>
#include <QCoreApplication>

void B::h() {
  qDebug("[%d] B::h() here!", (int) QThread::currentThreadId());
  QCoreApplication::instance()->quit();
}

void A::doit() {
  qDebug("[%d] emitting...", (int) QThread::currentThreadId());
  emit x();
}

int main(int argc, char* argv[]) {
  QCoreApplication app(argc, argv);
  A a;
  B b;
  QObject::connect(&a, SIGNAL(x()), &b, SLOT(h()));
  QThread t;
  t.start();
  b.moveToThread(&t);
  a.doit();
  t.wait();
  return 0;
}

一切都很好,只有最后的 t.wait() 永远不会返回。我的理解是调用 quit() 应该停止事件循环,这意味着 exec() 应该返回,run() 应该返回并且线程执行应该停止。我错过了什么吗?

【问题讨论】:

  • 你的方法的名字应该明确他们的目的。
  • 别担心,这不是生产代码!我发现假名在简短的示例/测试代码中比人为的描述性名称更有效。
  • 我同意 UmNyobe。如果您使用更多信息的名称,将更容易阅读和理解示例代码。例如。 A::doit() -> A::emitThreadStart(), void x() -> startThread(), void h() -> void quitApplication() ...等
  • 也许你是对的,因为这最终发布在公共论坛上。我喜欢在自己的测试中使用短名称,但这可能不适合它。

标签: multithreading qt signals-slots


【解决方案1】:

QCoreApplication::quit () 未声明为线程安全方法,因此您不能从另一个线程调用它。您的应用程序可能会崩溃或出现未定义的行为 (UB)。

t.wait() 永远不会返回,因为正在运行的线程一直在等待事件。要停止线程,您必须调用QThread::quit () [slot]

如果你想在工作完成后退出应用程序,你必须发出一个连接到QCoreApplication::quit () [static slot]的信号

如果你想在工作完成后停止工作线程,你还必须发出一个信号,连接到void QThread::quit () [slot]

添加:Threads, Events and QObjects 供进一步阅读

重要提示:您必须调用QCoreApplication::exec() 才能在线程、排队连接之间使用信号和槽机制。

来自 Qt QThread 文档:

每个 QThread 都可以有自己的事件循环。您可以启动事件循环 通过调用 exec();你可以通过调用exit() 或quit() 来停止它。有 线程中的事件循环可以连接来自 其他线程到此线程中的插槽,使用称为 queueed 的机制 连接。它还可以使用需要 线程中的事件循环,例如 QTimer 和 QTcpSocket。笔记, 但是,不能在 线程。

Qt::QueuedConnection 文档:

当控制返回到 接收者线程的事件循环。该插槽在 接收者的线程。

【讨论】:

  • 你是对的。我没有停止正确的事件循环。不幸的是,UmNyobe 在你之前指出了这一点,所以我不得不接受他们的回复。不过,我赞成您的回复,因为它更清楚。谢谢。
  • 再想一想,我想我最好接受你的回答,因为另一个有很多错误信息,可能会让其他阅读它的人感到困惑。
  • 关于您添加的通知,在我的代码 sn-p 中,我没有调用 QCoreApplication::exec(),但信号/插槽确实有效。在我看来,QThread 内的事件循环就足够了。
  • 那是因为您没有将事件从您的线程发布到核心应用程序。尝试从连接到应用程序槽的线程发出信号。
  • 我的理解是,我只需要一个事件循环,我想有插槽。因为我不需要在我的应用程序的主线程中处理信号(我正在处理的那个,而不仅仅是这个简化的示例),所以工作线程中的一个事件循环就可以了。所以简而言之,QCoreApplication::exec() 并不总是像你所说的那样需要。
【解决方案2】:

您的代码似乎有很多问题。

  • 你不打电话给app.exec()。这意味着没有主事件循环。不会发出Ax 信号。
  • 默认情况下,Qt 中的线程has it's own even loop(至少从几年前开始)。然后启动线程调用Qthread::run(),并启动事件循环。那是您的帖子所在的位置,而不是 t.wait()
  • t.wait() 的用途是什么?我相信你在滥用它。
  • (如果一切正常),在B::h() 中,您正在从另一个线程停止主线程。这是你想做的吗?

所以我的第一个建议是添加app.exec(),看看它的表现如何。解释你正在尝试做什么,并重写其他内容。因为you're doing it wrong

【讨论】:

  • t.start() 调用默认实现调用 exec() 的 run() 方法。正如我所说,信号 被正确发射和接收。 wait() 相当于 pthread_join,或者文档说,所以我所做的就是等待线程完成。最后,你的最后一点是正确的。我没有停止线程事件循环。将该行更改为QThread::currentThread()-&gt;quit() 就可以了。谢谢。
  • exec() 继续运行,所以 wait() 没有任何效果。
  • 当然是这样,但是wait()的目的是等待exec()结束,所以使用wait()本身并没有错;不停止正确的事件循环是。
  • 由于我没有使用主事件循环,我真的需要 app.exec() 吗?似乎没有它一切正常。
最近更新 更多