【问题标题】:Qt connection type between threads: why does this work?线程之间的 Qt 连接类型:为什么会这样?
【发布时间】:2013-12-18 17:16:00
【问题描述】:

在尝试使多摄像头系统与处理不同摄像头的不同线程一起工作时,我无法让信号和插槽在不同线程之间正常工作。我知道发送信号的对象和相关插槽的对象存在于不同的线程中这一事实有问题,因此我知道我可能只需要为连接找到一个合适的“连接类型”参数。最终,我发现只有使用 Qt::DirectConnection 才能让一切正常运行。

找到下面的简化代码。以下是关于一切应该如何工作的简短说明。

  • 应用程序是应该创建所有线程并启动它们的主程序。在这个简化版本中,它只是等待工作人员通过槽“退出”完成其工作。

  • Worker 是执行线程任务之一的对象。在这个简化的例子中,我只是在完成计算之前等待一些时间。然后,worker 发出一个指向应用程序实例的信号,然后允许该应用程序等待所有线程并退出 QCoreApplication。

我发现如果我在第二次连接中不使用Qt::DirectConnection,那么worker的finished()信号不会触发线程的quit()槽,这意味着应用然后保持挂起等待线程。

我的问题是:为什么会这样?由于这两个对象(工作者和线程)属于不同的线程,我不应该使用 QueuedConnection 还是其他什么?我认为 DirectConnection 应该只用于属于同一线程的对象。

main.cpp:

#include <iostream>
#include <QCoreApplication>
#include "program.h"

using namespace std;

int main(int argc, char **argv) {
  QCoreApplication app(argc, argv);

  Program *p = new Program;
  p->execute();

  app.exec();

  delete p;
}

程序.h

#ifndef _PROGRAM_H_
#define _PROGRAM_H_

#include <QThread>
#include <QTimer>
#include "worker.h"

class Program: public QObject {
  Q_OBJECT

  private:
    Worker *worker;
    QThread *thread;

  public:
    void execute();

  public slots:
    void quit();
};

#endif // _PROGRAM_H_

程序.cpp

#include "worker.h"

using namespace std;

void Program::execute() {
  worker = new Worker();
  thread = new QThread;
  worker->moveToThread(thread);

  cout << "Connection established: "
        << connect(thread, SIGNAL(started()), worker, SLOT(process()))
        << endl;

  // slot not called if I remove the fifth parameter
  // or if I put Qt::QueuedConnection
  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
                    Qt::DirectConnection)
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), this, SLOT(quit()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
        << endl;

  thread->start();
}

void Program::quit() {
  cout << "waiting.." << endl;
  thread->wait();
  cout << "           .. I'm done!" << endl;

  cout << "quitting from all.." << endl;
  QCoreApplication::quit();
  cout << "           .. I'm done!" << endl;
}

#include "program_moc.cpp"

worker.h

#ifndef _WORKER_H_
#define _WORKER_H_

#include <QObject>

class Worker: public QObject {
  Q_OBJECT

  public slots:
    void process();

  signals:
    void finished();
};

#endif // _WORKER_H_

worker.cpp:

#include <iostream>
#include <unistd.h>
#include "worker.h"

using namespace std;

void Worker::process() {
  cout << "Worker::process() started" << endl;
  usleep(1000000);

  cout << "Worker::finished() being emitted" << endl;
  emit finished();

  cout << "Worker::process() finished" << endl;
}

#include "worker_moc.cpp"

编辑

按照@ariwez 的回答确实解决了这个特定简化示例中的问题,但它并没有解决我现在添加的稍微复杂一点的问题。

在这个例子中,

  • 程序有自己的作业,通过使用 QTimer 定期执行。 Program还有另外一个QTimer,用来模拟用户退出程序,触发slot Program::quit()的执行。

  • Worker 执行自己的工作,直到他的退出标志设置为 false。这是在 Program::quit() 中完成的。

和前面的例子一样,worker 成功地完成了它的过程并发出了 finished() 信号,该信号也应该与线程的 quit() 槽连接。但是,不知何故不能执行槽,因为程序挂起等待线程。与前面的示例不同,重新定位 moveToThread 过程并不能解决问题:当且仅当我使用 Qt::DirectConnection 类型来连接 Worker::finished() 和 QThread::quit() 时,一切正常,并且我不明白为什么。

main.cpp:同上

程序.h:

#ifndef _PROGRAM_H_
#define _PROGRAM_H_

#include <QThread>
#include <QTimer>
#include "worker.h"

class Program: public QObject {
  Q_OBJECT

  private:
    QTimer *timer, *quittingTimer;
    Worker *worker;
    QThread *thread;

  public:
    ~Program();

    void execute();

  private slots:
    void quit();
    void update();
};

#endif // _PROGRAM_H_

程序.cpp:

#include <iostream>
#include <QCoreApplication>
#include "program.h"
#include "worker.h"

using namespace std;

Program::~Program() {
  delete timer;
  delete quittingTimer;
}

void Program::execute() {
  timer = new QTimer();
  timer->setInterval(500);
  connect(timer, SIGNAL(timeout()), this, SLOT(update()));

  worker = new Worker;
  thread = new QThread;

  cout << "Connection established: "
        << connect(thread, SIGNAL(started()), worker, SLOT(process()))
        << endl;

  // doesn't work if I remove Qt::DirectConnection
  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(quit()),
                    Qt::DirectConnection)
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), this, SLOT(quit()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater()))
        << endl;

  cout << "Connection established: "
        << connect(worker, SIGNAL(finished()), thread, SLOT(deleteLater()))
        << endl;

  worker->moveToThread(thread);

  timer->start();
  thread->start();

  // simulates user pressing key to close program
  quittingTimer = new QTimer();
  quittingTimer->singleShot(4000, this, SLOT(quit()));
}

void Program::quit() {
  cout << "timer->stop()" << endl;
  timer->stop();

  cout << "worker->quit()" << endl;
  worker->quit();

  cout << "thread->wait()" << endl;
  thread->wait();

  cout << "qcore->quit()" << endl;
  QCoreApplication::quit();
}

void Program::update() {
  cout << "Program::update() called" << endl;
}

#include "program_moc.cpp"

worker.h:

#ifndef _WORKER_H_
#define _WORKER_H_

#include <QObject>

class Worker: public QObject {
  Q_OBJECT
  private:
    bool quit_flag;

  public:
    void quit();

  public slots:
    void process();

  signals:
    void finished();
};

#endif // _WORKER_H_

worker.cpp:

#include <iostream>
#include <unistd.h>
#include <QThread>
#include "worker.h"

using namespace std;

void Worker::quit() {
  quit_flag = true;
}

void Worker::process() {
  quit_flag = false;
  while(!quit_flag) {
    cout << "Worker::process() is processing" << endl;
    usleep(300000);
  }
  cout << "Worker::finished() is being sent" << endl;
  emit finished();
  cout << "Worker::finished() is sent" << endl;
}

#include "worker_moc.cpp"

编辑 2

重新阅读@ariwez 链接中的文章,我发现这是第二个示例中的问题所在。问题是主事件循环在等待线程的 QThread::finished() 信号时被中断,因此无法将 Worker::finished() 信号分派到 QThread::quit() 槽中。所以,是的,基本上我自己陷入了僵局。

【问题讨论】:

  • 也许这可以帮助你:qt-project.org/wiki/Threads_Events_QObjects
  • @ariwez:谢谢,我想我找到了问题所在。我已经读过那篇文章,但我决定再读一遍,以确保我正确理解了所有内容。我将用正确的分析更新我的问题。如果你写另一个答案(你之前的答案发生了什么?)我会选择它。
  • 很高兴为您提供帮助。我不得不重新考虑我的答案,所以我把它隐藏了(希望我或其他人会给出更好的解决方案);)
  • @user2152106 我最近也有同样的问题。我在这里没有看到明确的答案。你的意思是我们不必QThread.wait(),只需设置工人的quit_flag = true?防止死锁?

标签: c++ qt signals-slots qthread


【解决方案1】:

在连接之前你 moveToThread 这就是为什么一切都在一个线程中。

【讨论】:

  • 所以你是说worker并没有真正在另一个线程中执行?或者即使对象“worker”正在另一个线程中执行,消息传递也以某种方式在一个线程中执行?有点迷糊,能详细点吗?顺便提一句。这个建议(在连接调用后重新定位 moveToThread)在这个简化的示例中确实有效,但它在稍微不那么简化的示例中不起作用。我将编辑问题并添加另一个示例,其中重新定位 moveToThread 不起作用,但使用Qt::DirectAccess 仍然可以。
  • 由于@ariwez 在问题中的评论而得到答案。
【解决方案2】:

我想我知道问题出在哪里了。

您的直接连接,这确实是一件坏事,因为它就像一个中断 - 立即执行。但这是因为它立即执行,它适用于您的情况。当线程关闭时,它会发出 quit() 信号......然后通常它会“退出”,但在 direcetConnection 中它会立即执行 slotfinished() 然后退出。

如果您有一个 QueuedConnection,那么它将向队列发出信号,然后完成“退出”。但是,一旦线程停止,线程队列就永远不会服务,因此您的finished() 函数永远不会运行。

也许更好的方法是连接到您的对象finished() 在那里发送您的信号,然后在线程中获取对象以停止线程本身。 (即不要远程停止线程,停止对象,然后停止其线程)。

【讨论】:

  • 哎呀...我想我的信号/槽写错了,但原因还是一样。
猜你喜欢
  • 1970-01-01
  • 2011-02-28
  • 1970-01-01
  • 2022-07-29
  • 1970-01-01
  • 1970-01-01
  • 2022-07-26
  • 2012-03-23
相关资源
最近更新 更多