c++ - 在 Qt 中使用排队连接时如何压缩插槽调用?

标签 c++ qt qthread qtcore qt-signals

看了一些文章like this关于 Qt Signal-Slot 通信 我还有一个关于排队连接的问题。

如果我有一些线程一直在互相发送信号,让我们说一个 thread_slow在它的事件循环中运行一个缓慢的方法和另一个 thread_fast正在运行一个发送多个信号的快速方法,而另一个线程仍在运行它的慢方法......当来自 thread_slow 的慢方法时返回到事件循环,它会不会处理之前由thread_fast发送的所有信号?还是只是最后一个(所有信号都是相同类型的)?

如果它会处理所有的信号,有没有办法让thread_slow 只处理最后一个 ? (考虑到多线程应用程序中的“最后一个”可能含糊不清,为了简单起见,让我们考虑线程请求最后一个信号之前的最后一个信号,因此在线程寻找最后一个信号时发送的新信号可能会丢失)。

(我问这个是因为我有多个线程从多个线程接收数据,我不希望它们处理旧数据,只是发送的最后一个数据)

我已经运行了一些测试,看起来 Qt 会处理所有的信号。我做了一个线程:

while(true)
{
    QThread::msleep(500);
    emit testQueue(test);
    test++;
}

另一个插槽将执行以下操作:
void test::testQueue(int test)
{
    test.store(private_test.load() + test);
    emit testText(QString("Test Queue: ") + QString::number(private_test.load()));
}

线程将运行:
while(true)
{
    QThread::msleep(3000);
    QCoreApplication::processEvents();
    private_test.store(private_test.load() + 1000);
}

我每 500 毫秒从一个线程向另一个线程发送一个信号,另一个线程休眠 3000 毫秒(3 秒),然后唤醒并将内部变量增加 100。每次执行插槽时,它都会发出一个文本收到的值 + 内部变量。我得到的结果是每次 QCoreApplication::processEvents();被调用,所有信号都被执行....(我编辑了这部分是因为我在之前的代码中发现了一个错误)

最佳答案

我试图将我的评论变成一个答案。我同意您的意见,即文档缺乏这些信息,或者至少对我来说不清楚,显然对您来说也是如此。

有两种选择可以获取更多信息:

1) 试用

将 qDebug() 或 printf()/fprintf() 语句放入“慢”线程中的插槽中,看看它打印出什么。运行几次并得出结论。

2) 确保

您需要阅读元对象编译器的源代码,也就是。 moc 从源文件中获取此信息。这是一个更复杂的调查,但这可能会导致确定性。

据我所知,每个信号发射都会发布一个相应的事件。然后,该事件将排队等待线程类中的单独线程。在这里你可以找到相关的两个源代码文件:

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)



class QPostEventList : public QVector

有两种方法可以权衡它们:

从数据修改器槽中排队一个繁忙的槽操作

主要优点是在忙操作期间信号不会丢失。但是,这本身可能会较慢,因为它可能会处理比需要更多的操作。

这个想法是为每个处理的事件重新设置数据,但真正的繁忙操作只排队执行一次。如果有更多事件,它不一定是第一个事件,但这是最简单的实现。

Foo::Foo(QObject *parent) : QObject(parent)
{
    ...
    connect(this, SIGNAL(dataUpdateSignal(const QByteArray&)), SLOT(dataUpdateSlot(const QByteArray&)));
    connect(this, SIGNAL(queueBusyOperationSignal()), SLOT(busyOperation()));
    ...
}

void Foo::dataUpdateSlot(const QByteArray &data)
{
    m_data = data;

    if (busyOperationQueued);
        emit queueBusyOperationSignal();
        m_busyOperationQueued = true;
    }
}

void MyClass::busyOperationSlot()
{

    // Do the busy work with m_data here

    m_busyOperationQueued = false;    
}

连接/断开

这个想法是在开始处理时将插槽与相应的信号断开。这将确保不会捕获新的信号发射,并在线程有空处理下一个事件时再次将插槽连接到信号。

尽管在连接和下一个偶数处理之间,这将在线程中有一些空闲时间,但至少这将是实现它的简单方法。根据此处未真正提供的更多上下文,性能差异实际上甚至可以忽略不计。

主要缺点是这会在繁忙操作期间丢失信号。
Foo::Foo(QObject *parent) : QObject(parent)
{
    ...
    connect(this, SIGNAL(dataUpdateSignal(const QByteArray&)), SLOT(busyOperationSlot(const QByteArray&)));
    ...
}

void MyClass::busyOperationSlot(const QByteArray &data)
{
    disconnect(this, SIGNAL(dataUpdateSignal(const QByteArray&)), this, SLOT(dataUpdateSlot(const QByteArray&)));

    // Do the busy work with data here

    connect(this, SIGNAL(dataUpdateSignal(const QByteArray&)), SLOT(dataUpdateSlot(const QByteArray&)));
}

future 的想法

我在想是否有一个方便的 API - 例如一个 processEvents() 类似的方法,但有一个参数来处理最后发布的事件 - 实际上明确地告诉事件系统处理最后一个而不是绕过问题本身。它看起来确实是这样的 API,但是,它是私有(private)的。

也许,有人会提交一个功能请求,以便在公共(public)场合拥有类似的东西。
/*!
\internal
Returns \c true if \a event was compressed away (possibly deleted) and should not be added to the list.
*/
bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventList *postedEvents)

相关源码可以在here找到.

它似乎在 QGuiApplication 中也有一个覆盖版本和 QApplication .

至于完整性,还有这样的方法:

void QCoreApplication::removePostedEvents(QObject * receiver, int eventType = 0) [static]

Removes all events of the given eventType that were posted using postEvent() for receiver.

The events are not dispatched, instead they are removed from the queue. You should never need to call this function. If you do call it, be aware that killing events may cause receiver to break one or more invariants.

If receiver is null, the events of eventType are removed for all objects. If eventType is 0, all the events are removed for receiver. You should never call this function with eventType of 0. If you do call it in this way, be aware that killing events may cause receiver to break one or more invariants.



但是,根据文档,这并不是您想要的。

关于c++ - 在 Qt 中使用排队连接时如何压缩插槽调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20866996/

相关文章:

c++ - 是否需要在头文件中定义初始化列表?

c++ - 访问外部窗口句柄

c++ - 部署不起作用且可执行扩展

c++ - QtSerialPort 在错误的线程中实例化,导致信号/插槽失败

c++ - 在 Qt 中,当线程结束时对象会发生什么?

c++ - 我可以使用 %p 说明符输出 Windows 句柄吗?

c++ - 如何开始跨平台网络编程?

c++ - 字符串(__DATE__)中的QDateTime无效

c++ - Qt/C++ 图像不出现在屏幕上

c++ - QAudioOutput 代码是否存在内存泄漏?