我有三个QThread运行三个事件循环。
在每个线程中,我有一个对象,这些对象有信号和插槽。
我完全理解一个信号和一个插槽之间的连接是如何工作的:
// This connection would execute theSlotB in ThreadA
connect(objectA, SIGNAL(theSignalA()), objectB, SLOT(theSlotB()), Qt::DirectConnection);
// This connection would post an event in ThreadB event loop, which will execute theSlotB
connect(objectA, SIGNAL(theSignalA()), objectB, SLOT(theSlotB()), Qt::QueuedConnection);
我问的是这样的事情的行为:
auto sig2sig = Qt::DirectConnection;
auto sig2slot = Qt::DirectConnection;
connect(objectA, SIGNAL(theSignalA()), objectB, SIGNAL(theSignalB()), sig2sig);
connect(objectB, SIGNAL(theSignalB()), objectC, SLOT(theSlotC()), sig2slot);
theSlotC
和sig2sig
的不同可能值的执行位置sig2slot
。
sig2sig = DirectConnection
,sig2slot= DirectConnection
,
theSignalA
和theSignalB
之间的DirectConnection?sig2sig = DirectConnection
,sig2slot= QueuedConnection
,
theSignalA
和theSignalB
之间的排队连接?sig2sig = QueuedConnection
,sig2slot= DirectConnection
,
theSlotC
执行了ThreadB
?sig2sig = QueuedConnection
,sig2slot= QueuedConnection
,
theSlotC
执行了ThreadC
,但是在重新发出ThreadB
信号后发生了延迟?或者SIGNAL / SIGNAL连接可能刚被丢弃?
答案 0 :(得分:2)
信号之间的连接类型确定发出第二个信号的线程,只需将该信号视为执行其连接的插槽的另一个函数/插槽(完全相同{{3}申请):
Qt::DirectConnection
,则第二个信号始终从发出第一个信号的线程发出。Qt::QueuedConnection
,则当控制返回到接收方对象的线程的事件循环时,第二个信号总是排队等待调用。Qt::AutoConnection
,则在发出信号并忽略发送对象的线程时,将解析连接类型。
Qt::DirectConnection
相同。Qt::QueuedConnection
。我写了一个最小的测试来证明这件事:
#include <QtCore>
//QThread wrapper for safe destruction
//see http://stackoverflow.com/a/19666329
class Thread : public QThread{
using QThread::run; //final
public:
Thread(QObject* parent= nullptr): QThread(parent){}
~Thread(){ quit(); wait();}
};
class Worker : public QObject{
Q_OBJECT
public:
explicit Worker(QString name, QObject* parent= nullptr):QObject(parent){
setObjectName(name);
//the statement is printed from the thread that emits the signal
//since we don't provide a context object
connect(this, &Worker::workerSignal, [=]{
qDebug() << objectName() << "signal emitted from thread:"
<< QThread::currentThread()->objectName();
});
}
~Worker() = default;
Q_SIGNAL void workerSignal();
Q_SLOT void workerSlot(){
qDebug() << objectName() << "slot invoked in thread:"
<< QThread::currentThread()->objectName();
}
};
int main(int argc, char* argv[]){
QCoreApplication a(argc, argv);
//using the main thread as threadA
QThread::currentThread()->setObjectName("threadA");
Worker workerA("workerA");
//creating threadB and threadC
Thread threadB;
threadB.setObjectName("threadB");
Worker workerB("workerB");
workerB.moveToThread(&threadB);
Thread threadC;
threadC.setObjectName("threadC");
Worker workerC("workerC");
workerC.moveToThread(&threadC);
threadB.start(); threadC.start();
//change the following types to whatever case you want to test:
auto sig2sig= Qt::QueuedConnection;
auto sig2slot= Qt::QueuedConnection;
qDebug() << "sig2sig= " << sig2sig << ", sig2slot=" << sig2slot;
QObject::connect(&workerA, &Worker::workerSignal,
&workerB, &Worker::workerSignal, sig2sig);
QObject::connect(&workerB, &Worker::workerSignal,
&workerC, &Worker::workerSlot, sig2slot);
emit workerA.workerSignal();
//quit application after 0.5 second
QTimer::singleShot(500, &a, &QCoreApplication::quit);
return a.exec();
}
#include "main.moc"
这将按如下方式设置连接:
workerA::workerSignal() -------> workerB::workerSignal() -------> workerC::workerSlot()
每个工作人员都在自己的线程中,您可以通过更改分配给sig2sig
和sig2slot
变量的值来更改连接类型。以下是您要求的案例中的输出:
sig2sig = DirectConnection
,sig2slot= DirectConnection
:
一切都在threadA中作为直接函数调用执行。
"workerA" signal emitted from thread: "threadA"
"workerB" signal emitted from thread: "threadA"
"workerC" slot invoked in thread: "threadA"
sig2sig = DirectConnection
,sig2slot= QueuedConnection
:
信号在threadA
中作为直接函数调用执行。该插槽在threadC
中调用。
"workerA" signal emitted from thread: "threadA"
"workerB" signal emitted from thread: "threadA"
"workerC" slot invoked in thread: "threadC"
sig2sig = QueuedConnection
,sig2slot= DirectConnection
:
信号排队,并从threadB
发出。该插槽在threadB
中作为直接函数调用调用。
"workerA" signal emitted from thread: "threadA"
"workerB" signal emitted from thread: "threadB"
"workerC" slot invoked in thread: "threadB"
sig2sig = QueuedConnection
,sig2slot= QueuedConnection
:
信号排队,并从threadB
发出。插槽调用也排队并在threadC
中执行。所以,每件事都发生在右线程中,如果使用Qt::AutoConnection
,这将是相同的行为:
"workerA" signal emitted from thread: "threadA"
"workerB" signal emitted from thread: "threadB"
"workerC" slot invoked in thread: "threadC"
答案 1 :(得分:0)
在qt中,信号只是另一个函数,当被调用时,通过所请求的方法调用插槽,即
当您将信号作为插槽连接时,它的行为类似于插槽,即通过请求的方法调用/发出。请注意,实际发射是线程安全的,因此只要通过AutoConnection或QueuedConnection连接插槽,就可以从任何线程直接调用/发出信号(参见Signals and Slots Across Threads)。
一些例子:
当前线程(A)中调用的所有内容。 slot需要是线程安全的。
signalB在线程A中发出,slotC正确排队以在其自己的线程中执行 与sig2sig = DirectConnection相同,sig2slot = AutoConnection
signalB排队在线程B中发出。当发生这种情况时,slotC排队等待在线程C中执行 与sig2sig = AutoConnection相同,sig2slot = AutoConnection。
由于排队连接比直接连接慢得多,如果这是一个瓶颈,最好将sig2sig与DirectConnection连接,并将DirectConnections连接到signalB必须是线程安全的。
但是,如果只有两个线程,则应使用AutoConnection。然后只能有一个排队呼叫,另一个是直接呼叫。
答案 2 :(得分:0)
如果你看一下In MOC的对象,你会看到每个信号都有一个信号处理方法,它会激活(比如执行或发送事件)连接的插槽,当你发出一个信号时会调用这个方法,所以发射实际上是在线程中调用该对象的方法。实际上,信号处理程序只是另一个插槽。在信号到信号连接的情况下,将在调用线程中调用第一对象的信号处理程序并激活连接线程中的槽(即第二对象的信号处理程序)。激活的时隙可以是目标对象的时隙或其信号之一。激活确实关心发射器信号和目标插槽/信号之间的连接类型。当您将信号连接到另一个信号时,实际上您正在将信号连接到第二个对象的信号处理程序。
在DirectConnection中,将在发射器线程中调用插槽。 在QueuedConnection中,将在目标对象的线程中调用插槽。
因此,如果您将目标信号想象为另一个发出目标信号的插槽,我们将会:
sig2sig = Direct,sig2slot = Direct
sig2sig = Direct,sig2slot = Queued
sig2sig =已排队,sig2slot =直接
sig2Sig =已排队,sig2slot =已排队
此代码还验证了行为
#include <QObject>
#include <QDebug>
#include <QThread>
class XClass : public QObject
{
Q_OBJECT
public:
XClass(char ident){ this->ident=ident; this->moveToThread(&this->t); this->t.start(); }
char ident;
private:
QThread t;
signals:
void signalX();
public slots:
void printTid() { qDebug() << "Object " << this->ident << " lives in thread" << QThread::currentThreadId(); }
void slotX() { qDebug() << "Slot" << this->ident << " fired in thread" << QThread::currentThreadId(); }
void emitSignalX(){ qDebug() << "Signal" << this->ident << "emitng from thread" << QThread::currentThreadId(); emit signalX(); }
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
XClass A('A');
XClass B('B');
XClass C('C');
QMetaObject::invokeMethod(&A, "printTid", Qt::BlockingQueuedConnection);
QMetaObject::invokeMethod(&B, "printTid", Qt::BlockingQueuedConnection);
QMetaObject::invokeMethod(&C, "printTid", Qt::BlockingQueuedConnection);
QObject::connect(&A, &XClass::signalX, &B, &XClass::signalX, Qt::DirectConnection);
QObject::connect(&B, &XClass::signalX, &C, &XClass::slotX, Qt::DirectConnection);
//QObject::connect(&A, &XClass::signalX, &B, &XClass::signalX, Qt::DirectConnection);
//QObject::connect(&B, &XClass::signalX, &C, &XClass::slotX, Qt::QueuedConnection);
//QObject::connect(&A, &XClass::signalX, &B, &XClass::signalX, Qt::DirectConnection);
//QObject::connect(&B, &XClass::signalX, &C, &XClass::slotX, Qt::QueuedConnection);
//QObject::connect(&A, &XClass::signalX, &B, &XClass::signalX, Qt::QueuedConnection);
//QObject::connect(&B, &XClass::signalX, &C, &XClass::slotX, Qt::QueuedConnection);
QMetaObject::invokeMethod(&A, "emitSignalX");
return a.exec();
}