当接收器对象没有接收到信号时使用 QtDirectConnection
有时你看到发送器线程中发出了一个信号,但没有调用连接的槽(换句话说它没有接收到信号),你已经问过它并且最终得到连接类型 Qt::DirectConnection 会修复它,所以发现问题,一切都好。
但一般来说,使用 Qt:DirectConnection 是个坏主意,直到你真正知道这是什么,没有别的办法。让我们来解释一下,由 Qt 创建的每个线程(包括由 QThread 创建的主线程和新线程)都有 Event 循环,事件循环负责接收信号并在其线程中调用 aproporiate slot。通常在插槽内执行阻塞操作是不好的做法,因为它会阻止该线程的事件循环,因此不会调用其他插槽。
如果阻止事件循环(通过非常耗时或阻塞操作),则在解除事件循环之前,你将不会在该线程上接收事件。如果阻塞操作永远阻塞事件循环(例如忙碌),则永远不会调用槽。
在这种情况下,你可以在连接到 Qt::DirectConnection 时设置连接类型,现在即使事件循环被阻止,也会调用插槽。那怎么会破坏一切呢?在 Qt::DirectConnection 中,将在发射器线程中调用插槽,而不是接收器线程,它可能会破坏数据同步并遇到其他问题。所以永远不要使用 Qt::DirectConnection,除非你知道你在做什么。如果你的问题将通过使用 Qt::DirectConnection 解决,你必须仔细查看代码并找出阻止事件循环的原因。阻止事件循环不是一个好主意,而且在 Qt 中没有推荐它。
这是一个显示问题的小例子,你可以看到 nonBlockingSlot 甚至会被阻塞事件循环调用 while(1)
表示编码错误
class TestReceiver : public QObject{
Q_OBJECT
public:
TestReceiver(){
qDebug() << "TestReceiver Constructed in" << QThread::currentThreadId();
}
public slots:
void blockingSlot()
{
static bool firstInstance = false;
qDebug() << "Blocking slot called in thread" << QThread::currentThreadId();
if(!firstInstance){
firstInstance = true;
while(1);
}
}
void nonBlockingSlot(){
qDebug() << "Non-blocking slot called" << QThread::currentThreadId();
}
};
class TestSender : public QObject{
Q_OBJECT
public:
TestSender(TestReceiver * receiver){
this->nonBlockingTimer.setInterval(100);
this->blockingTimer.setInterval(100);
connect(&this->blockingTimer, &QTimer::timeout, receiver, &TestReceiver::blockingSlot);
connect(&this->nonBlockingTimer, &QTimer::timeout, receiver, &TestReceiver::nonBlockingSlot, Qt::DirectConnection);
this->nonBlockingTimer.start();
this->blockingTimer.start();
}
private:
QTimer nonBlockingTimer;
QTimer blockingTimer;
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TestReceiver TestReceiverInstance;
TestSender testSenderInstance(&TestReceiverInstance);
QThread receiverThread;
TestReceiverInstance.moveToThread(&receiverThread);
receiverThread.start();
return a.exec();
}