在 QObject 之间跨不同线程连接信号/插槽

Posted

技术标签:

【中文标题】在 QObject 之间跨不同线程连接信号/插槽【英文标题】:connecting signal/slot across different threads between QObjects 【发布时间】:2013-01-27 15:41:49 【问题描述】:

我想知道在 MainWindow 的构造器中创建的两个 QObjects 之间连接信号/插槽的最佳做法是什么,但后来移动到不同的线程...当我使用选项 Qt::Directconnection 连接时,默认连接似乎不起作用事情开始工作了...但有时信号/插槽会失败...以下是我的代码模式..如果我需要更改我的类设计,请告诉我...

MainWindow.cpp

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),

   myObjectA = new ObjectA;
   myObjectB = new ObjectB;

   connect( myObjectA,SIGNAL(signalA()),myObjectB,SLOT(slotB()) );

   myObjectA.initiateProcess();
   myObjectB.initiateProcess();

ObjectA.h

#include <QThread>
#include <QObject>

class ObjectA : public QObject

    Q_OBJECT
public:
    explicit ObbjectA(QObject *parent = 0);
    void inititateProcess();
public slots:
    void do_job();

signals:
    void signalA();
private:
    QThread *worker;

ObjectA.cpp

ObjectA::ObjectA(QObject* parent)

  ....


void ObjectA::do_jobA()

  //do something;


void ObjectA::initiateProcess()

  worker = new QThread;
  connect(worker,SIGNAL(started()),this,SLOT(do_jobA()));
  this->moveTo(worker);
  worker->start()

ObjectB.h

#include <QThread>
#include <QObject>

class ObjectB : public QObject

    Q_OBJECT
public:
    explicit ObjectB(QObject *parent = 0);
    void initiateProcess();
public slots:
    void do_job();
    void slotB();

signals:
    void signalB();//for some other slot
private:
    QThread *worker;

ObjectB.cpp

ObjectB::ObjectB(QObject* parent)

  ....


void ObjectB::do_jobB()

  //do something;


void ObjectB::initiateProcess()

  worker = new QThread;
  connect(worker,SIGNAL(started()),this,SLOT(do_jobB()));
  this->moveTo(worker);
  worker->start()

【问题讨论】:

您的代码对我有用。随机猜测可能有什么问题:do_jobA 或 do_jobB 是否可能没有终止,因此线程没有机会执行其插槽? 好的...这是一个很好的猜测,因为在其中一个 do_job 我有一个等待 QWaitcondition 的 while 循环...但我需要那个 while 循环来使一个线程中的对象等待一些来自不同线程的数据... 【参考方案1】:

一般来说,作为个人意见,即使在线程中,我也不会将阻塞代码(如QWaitCondition)与事件循环混合,除非您知道它不会长时间阻塞。对于 GUI 线程,我会说“long”超过 100 毫秒(比这更长,即使在传统的桌面应用程序中用户体验也开始受到影响),但对于其他线程,它可能要长得多,这取决于它可以阻塞多长时间线程需要处理的事件和信号。

对于多线程,对信号使用自动或显式排队连接通常会更好。直接连接将在发出信号的线程中执行,如果接收对象位于另一个线程中,则需要使插槽(以及因此类中相关的所有内容)成为线程安全的。不这样做会更简单、更安全,需要跟踪的事情更少。

如果您现在在一个线程中编写代码,但想准备稍后将其移动到另一个线程,那么最好让连接排队。这样一来,单线程的行为将基本相同,emit 将立即返回,以后不会有任何意外。

如果您想编写在任何线程中阻塞未确定或时间过长的代码,最好继承QThread,覆盖QThread::run(),并且永远不要在其中调用exec()。然后您可以在自己的循环中使用 QMutex 和 QWaitCondition,或者使用其他一些“传统”的线程间通信方法。您仍然可以从线程发出信号,但连接应该排队以避免线程问题。还要记住,你添加到 QThread 的任何槽都应该在 QThread 对象所在的线程中执行,而不是在 run() 方法正在执行的线程中执行。这实际上是一种非常方便的模式,您可以在 QThread 子类的槽中“隐藏”所有实际的线程交互代码(请记住,它们不会在运行 run() 的线程中执行),只要您注意不要锁定任何QMutex在这些地方用了太久了。

【讨论】:

【参考方案2】:

根据您的评论,您的线程正忙于QWaitCondition,因此无法处理信号。如果您真的需要QWaitCondition,您可以使用QCoreApplication::processEvents 和超时对插槽进行轮询,例如

while(true) 
    if(condition.wait(&mutex, 1000)) 
        // process condition
     else 
        QCoreApplication::processEvents()
    

但这涉及处理信号的延迟(本例中为 1 秒)。除此之外,还有一个类似的question on SO 建议去掉 QWaitCondition 并改用信号/插槽。

【讨论】:

以上是关于在 QObject 之间跨不同线程连接信号/插槽的主要内容,如果未能解决你的问题,请参考以下文章

Qt入门教程QObject篇线程和QOBject

Qt入门教程QObject篇线程和QOBject

两个不同类别的信号和插槽

暂时阻塞两个 QObject 之间的信号

Qt 插槽同时断开连接并从不同线程调用

C++ 和 QML:将 QML 信号连接到 C++ 插槽