在 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 之间跨不同线程连接信号/插槽的主要内容,如果未能解决你的问题,请参考以下文章