当接收器对象没有接收到信号时使用 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();
}