當接收器物件沒有接收到訊號時使用 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();
}