4

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

找到下面的简化代码。这是对一切应该如何工作的简短描述。

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

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

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

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

主.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"

工人.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_

工人.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"

工人.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_

工人.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() 槽中。所以,是的,基本上我自己陷入了僵局。

4

2 回答 2

2

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

于 2013-08-07T12:03:13.163 回答
1

我想我知道问题可能出在哪里。

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

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

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

于 2013-08-22T07:55:29.073 回答