信号和插槽的混乱行为取决于连接类型

时间:2018-12-30 15:38:21

标签: c++ multithreading qt signals-slots

我有以下代码段

#include <QObject>
#include <QtConcurrent>

class Foo : public QObject {
Q_OBJECT
public:
    explicit Foo(QObject *parent = nullptr) : QObject(parent) {
        connect(this, &Foo::signal, this, &Foo::slot, ConnectionType);
    }

    void startBlockingMap() {
        qDebug("startBlockingMap");
        slot_counter = 0;
        std::atomic_int signal_counter = 0;
        QtConcurrent::blockingMap(nums, [&](auto &&num) {
            ++signal_counter;
            emit signal();
        });
        qDebug("result: %d signals, %d slots", int(signal_counter), int(slot_counter));
        slot_counter = 0;
    }

public slots:
    void slot() { ++slot_counter; }

signals:
    void signal();

private:
    std::atomic_int slot_counter = 0;
    std::vector<int> nums{1, 2, 5, 8};
};

#include "main.moc"

int main(int argc, char *argv[]) {
    QCoreApplication app(argc, argv);
    Foo *foo = new Foo(&app);
    QTimer::singleShot(10, foo, [foo, &app]() {
        foo->startBlockingMap();
        app.quit();
    });
    return app.exec();
}

根据将哪种连接类型传递给connect,它会产生不同的结果。

ConnectionTypeQt::DirectConnection时,输出为

startBlockingMap
result: 4 signals, 4 slots

这很清楚。

ConnectionTypeQt::QueuedConnection时,输出为

startBlockingMap
result: 4 signals, 0 slots

不是。我以为,startBlockingMap完成后将执行插槽,但是根本不执行。为什么?发生什么事了?

最后,当ConnectionTypeQt::AutoConnection时的结果令人惊讶。我希望它们与Qt::DirectConnectionQt::QueuedConnectionas documentation states)相同,但是它们是不同的:

startBlockingMap
result: 4 signals, x slots

x的范围从0到4,具体取决于……某些因素。显然,存在一些数据争用(?)。我不知道。

为什么不使用Qt::QueuedConnection执行插槽? 为什么连接类型为Qt::AutoConnection时的输出与直接连接的排队时的输出不同?为什么这么随机?

编辑: 在this answer的帮助下,我看到要在blockingMap之后执行通过队列连接连接的插槽,应该通过调用qApp->processEvents()显式地继续事件循环。

1 个答案:

答案 0 :(得分:2)

  

不是。我以为,插槽会在地图完成后执行,但根本不会执行。为什么?发生什么事了?

当控件返回事件循环时,将执行插槽,但是此处startBlockingMap函数是从事件循环中调用的,因此,当startBlockingMap函数返回且控件进入时,插槽将被执行回到事件循环。

  

为什么连接类型为Qt :: AutoConnection时的输出不同于直接连接类型和排队时的输出?为什么这么随机?

blockingMap函数使用不同的线程来调用您的lambda,有时是线程池中的线程,有时是执行blockingMap函数的线程(此处为“主”线程)。您可以通过将行qDebug() << this->thread() << ' ' << QThread::currentThread();添加到lambda中来进行检查。现在,emit slot()有时是从不是对象所有者的函数执行的,因此信号会排队,有时是从作为对象所有者的“主”线程开始的,因此插槽可以直接执行您会看到控制台中的增加。