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