問題描述
在閱讀了一些關于 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_slow
is 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_fast
or 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) 確保
您需要閱讀元對象編譯器的源代碼,也就是.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 public QPostVEventListector/a>
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;
}
連接/斷開連接
思路是在開始處理的時候,把slot和對應的信號斷開.這將確保不會捕獲新的信號發射,并在線程有空處理下一個事件時再次將槽連接到信號.
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() 類似的方法,但有一個參數來處理最后一個發布的事件 - 實際上明確地告訴事件系統處理最后一個而不是繞過問題本身.它看起來確實是這樣的 API,但它是私有的.
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)
相關源碼可在這里.
它似乎在 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) [靜態]
刪除使用 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 中使用排隊連接時如何壓縮插槽調用?的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!