如何在Qt中使用排队连接时压缩槽调用? [英] How to Compress Slot Calls When Using Queued Connection in Qt?

查看:223
本文介绍了如何在Qt中使用排队连接时压缩槽调用?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

阅读了一些关于Qt Signal-Slot通信的喜欢这篇文章之后,一个关于队列连接的问题。

After reading some articles like this about Qt Signal-Slot communications I still have a question concerning the queued connection.

如果我有一些线程发送信号到对方,让一个 thread_slow 在它的事件循环中运行一个慢方法,另一个 thread_fast 正在运行一个快速发送多个信号,而另一个线程仍然运行它的慢方法... ..当慢方法从 thread_slow 返回到事件循环时,它将处理之前由 thread_fast 或只是最后一个(所有的信号是相同的类型)?

If I have some threads sending signals all the time to each other and lets say one thread_slowis running a slow method in it's event loop and another thread_fast is running a fast one that sends multiple signals while the other thread is still running it's slow method.....when the slow method from thread_slow returns to the event loop, will it process all the signals that were sent before by thread_fastor just the last one (all the signals are the same type)?

如果它将处理所有的信号,是否有一种方法使 thread_slow 最后一个? (考虑到多线程应用程序中的最后一个可能模糊,让我们考虑在线程请求最后一个信号之前的最后一个信号,为了简单起见,因此在线程寻找最后一个信号时发送新的信号可能会丢失)。

If it will process all the signals, is it there a way to make the thread_slow only process the last one? (Considering "the last one" in a multithread application might be vague, let's consider the last signal before the thread asked for the last signal, for the sake of simplicity, so new ones being sent while the thread looks for the last might be lost).

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

(I am asking this because I have multiple threads receiving data from multiple threads, and I dont want them to process old data, just the last one that was sent)

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

I have run some tests, and it appears that Qt will process all the signals. I made one thread do:

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(); 被调用,所有的信号都被执行....(我编辑这部分,因为我发现一个错误在我以前的代码)

I am sending a signal from one thread to the other every 500 milliseconds, and the other thread sleeps for 3000 milliseconds (3 seconds) and then wakes up and increment an internal variable by 100. every time the slot is executed it emits a text with the value received + the internal variable. The result I am having is that every time QCoreApplication::processEvents(); is called, all signals are executed....(I edited this part because I found a bug in my previous code)

推荐答案

我试图形成我的评论到答案。我同意你的文档缺少这些信息,或者至少对我来说不清楚,显然对你也不清楚。

I am trying to form my comment into an answer. I agree with you about that the documentation is lacking this information, or at least it is not clear for me, and apparently for you either.

有两个选项获取更多信息:

There would be two options to get more information:

1)试用

放置一个qDebug()或printf()/ fprintf语句插入你的插槽中的慢线程,看看它打印出来。运行这几次并得出结论。

Put a qDebug() or printf()/fprintf() statement into your slot in the "slow" thread and see what it prints out. Run this a few times and draw the conclusion.

2)确保

您需要阅读源代码为这个如何实现元对象编译器,aka。 moc从源文件获取这个。这是一个更多的参与调查,但这可能导致确定性。

You would need to read the source code for this how the meta object compiler, aka. moc gets this through from the source file. This is a bit more involved investigation, but this could lead to certainity.

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

As far as I know, every signal emission posting a corresponding event. Then, the event will be queued for the separate thread within the thread class. Here you can find the relevant two source code files:

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

class QPostEventList:public QVector

有两种方法可以权衡:

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

The main advantage is that signals could not be lost during the busy operation. However, this could be inherently slower as it can potentially process a lot more operation than needed.

这个想法是重新设置每个事件处理的数据,实际忙操作仅排队执行一次。

The idea is that the data is re-set for each event handled, but the real busy operation is queued for execution only once. It does not necessarily have to be the for the first event if there are more, but that is the simplest implementation.

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;    
}



连接/断开



这个想法是在开始处理时从相应的信号中断开插槽。这将确保新的信号发射不会被捕获,并且一旦线程可以自由处理下一个事件,再次将插槽连接到信号。

Connect/Disconnect

The idea is to disconnect the slot from the corresponding signal when starting the processing. This will ensure that new signal emission would not be caught, and connect the slot to the signal again once the thread is free to process the next events.

这将有一些空闲时间在线程虽然之间的连接和下一个甚至处理,但至少这将是一个简单的方式。实际上,甚至可以忽略的性能差异取决于更多的上下文,这里没有真正提供。

This would have some idle time in the thread though between the connection and the next even handled, but at least this would be a simple way of implmeneting it. It may actually be even negligible a performance difference depending on more context not really provided here.

主要缺点是在忙碌操作期间会丢失信号。

The main drawback is that this would lose the signals during the busy operation.

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&)));
}



未来的想法



我在想如果有一个方便的API - 例如一个processEvents()方法,但是带有一个参数,只处理最后一个事件 - 实际上告诉事件系统显式地处理最后一个事件,而不是规避问题本身。

Future thoughts

I was thinking if there was a convenient API - e.g. a processEvents() alike method, but with an argument to process only the last event posted - for actually telling the event system explicitly to process the last one rather than circumventing the issue itself. It does appear to be such an API, however, it is private.

也许,有人会提交一个功能请求,在公共场合有类似的东西。

Perhaps, someone will submit a feature request to have something like that in 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)

可以找到相关的源代码此处

The relevant source code can be found here.

QGuiApplication 中似乎还有一个覆盖版本 QApplication

It also seems to have an overriden version in QGuiApplication and QApplication.

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

As for completeness, there is also such a method like this:


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

删除使用postEvent()发布的给定eventType的所有事件

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.

如果接收器为空,则eventType的事件将被删除,所有对象都将被删除。 。如果eventType为0,则为接收器删除所有事件。你不应该调用eventType为0的这个函数。如果你这样调用它,请注意,杀死事件可能会导致接收者中断一个或多个不变量。

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.

但这不是你想在这里按照文档。

But this is not quite what you would like to have here as per documentation.

这篇关于如何在Qt中使用排队连接时压缩槽调用?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆