为什么我不能在信号上启动 QThread?

Why can't I start a QThread on a signal?

我想在另一个 QThread 启动时启动一个 QThread,但它不起作用。

main.cpp 片段

Worker stat_worker;
stat_worker.moveToThread(stat_worker.stat_thread);

Worker some;
some.moveToThread(some.somethread);
QObject::connect(stat_worker.stat_thread, SIGNAL(started()), some.somethread, SLOT(start()));
QObject::connect(some.somethread, SIGNAL(started()), &some, SLOT(print_some()));
stat_worker.stat_thread->start();

worker.h

class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker();

    QThread *stat_thread = new QThread;
    QThread *somethread = new QThread;
signals:
//some signals
    void start_thread();
public slots:
//some slots
    void print_some();
    void somethread_starter();
};

#endif // WORKER_H

worker.cpp相关函数

void Worker::print_some()
{
    qInfo() << "-somethread started() signal arrived!";
}

当我尝试通过单击按钮启动线程时,它也不起作用。

甚至创建一个启动线程的槽:

QObject::connect(stat_worker.stat_thread, &QThread::started, &some, &Worker::somethread_starter);

void Worker::somethread_starter()
{
    qInfo() << "-I got started by another thread!";
    somethread->start();
}

或启动另一个线程时发出的信号:

void Worker::wip_status(){
    emit start_thread();
}

QObject::connect(stat_worker.stat_thread, &QThread::started, &stat_worker, &Worker::wip_status);
QObject::connect(&stat_worker, &Worker::start_thread, &some, &Worker::somethread_starter);

工作。

提前感谢您回复我的post。

我尝试用我自己的 MCVE(只是短了一点)重现 OPs 问题。

#include <QtWidgets>

struct Worker: QObject {
  QString name;
  QThread qThread;

  Worker(const QString &name): name(name)
  {
    moveToThread(&qThread);
    connect(&qThread, &QThread::finished, this, &Worker::reportFinished);
  }

  void start()
  {
    qDebug() << "Start" << name;
    qThread.start();
  }

  void reportFinished()
  {
    qDebug() << "Exit" << name;
  }
};

// main application
int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QCoreApplication app(argc, argv);
  Worker worker1("worker 1");
  Worker worker2("worker 2");
  // install signal handlers
  QObject::connect(&worker1.qThread, &QThread::started, &worker2, &Worker::start);
  worker1.start();
  // runtime loop
  return app.exec();
}

输出:

Qt Version: 5.13.0
Start "worker 1"

这是 OP 观察到的。那么,什么?

  • worker1.qThread.started 信号连接到 worker2.start 插槽
  • worker1 已启动
  • worker2 好像没有启动。

是什么让我产生了怀疑:moveToThread()

目的是将 Worker 对象与其成员 QThread 相关联。

我不确定的是:在 QThread 开始之前这可能吗?

为了检查这一点,我评论了 moveToThread():

  Worker(const QString &name): name(name)
  {
    //moveToThread(&qThread);
    connect(&qThread, &QThread::finished, this, &Worker::reportFinished);
  }

输出:

Qt Version: 5.13.0
Start "worker 1"
Start "worker 2"

我评论moveToThread()的原因: qThread::start() 的调用应该发生在主应用程序(线程)的上下文中。 所以,将 worker2 移动到它的 QThread 意味着信号被发送到 worker2.qThread 的事件循环——实际上还没有开始。

因此,无法处理该事件。

moveToThread() 应该稍后完成 – 例如响应 started() 信号:

#include <QtWidgets>

struct Worker: QObject {
  QString name;
  QThread qThread;

  Worker(const QString &name): name(name)
  {
    connect(&qThread, &QThread::started, this, &Worker::moveThisToThread);
    connect(&qThread, &QThread::finished, this, &Worker::reportFinished);
  }

  void start()
  {
    qDebug() << "Start" << name;
    qThread.start();
  }

  void moveThisToThread()
  {
    moveToThread(&qThread);
    qDebug() << name << "associated to its thread, from now.";
  }

  void reportFinished()
  {
    qDebug() << "Exit" << name;
  }
};

// main application
int main(int argc, char **argv)
{
  qDebug() << "Qt Version:" << QT_VERSION_STR;
  QCoreApplication app(argc, argv);
  Worker worker1("worker 1");
  Worker worker2("worker 2");
  // install signal handlers
  QObject::connect(&worker1.qThread, &QThread::started, &worker2, &Worker::start);
  worker1.start();
  // runtime loop
  return app.exec();
}

输出:

Qt Version: 5.13.0
Start "worker 1"
"worker 1" associated to its thread, from now.
Start "worker 2"
"worker 2" associated to its thread, from now.

加分题:

So does that mean "QThread::start" is useless as receiver of a signal?

不,不是。即使没有具有该签名的现有信号(我知道),应用程序开发人员也可以自由地“发明”一个。

但是,请记住 Qt5 实际上并不需要明确标记 SLOTs 才能将它们用于信号,过去可能会找到更明显的答案:

对于 Qt4 信号,QThread::start 插槽可以直接连接到 QThread::started 信号。 (QThread::start中唯一一个参数默认值生效。)

由于我没有使用 Qt4 信号的经验(我从 Qt5 开始),我修改了我的示例代码以证明我是正确的:

  QObject::connect(&worker1.qThread, SIGNAL(started()), &worker2.qThread, SLOT(start()));

输出:

Qt Version: 5.13.0
Start "worker 1"
"worker 1" associated to its thread, from now.
"worker 2" associated to its thread, from now.

Start "worker 2" 不再发出,因为 worker1.started() 现在直接调用 worker2.qThread.start()

因此,对于 Qt4 信号,OP 的原始代码可能已经生效。 不是信号和插槽的不兼容(正如有人猜测的那样)导致了这个问题,但可能是上面描述的 moveToThread() 问题(以及)并没有使它令人满意地工作。