Return value from slot in differnet thread
-
Hi
I have a function I use to init a socket in a different thread/class. I'd like to get a return value that tells me in the main/GUI thread if everything is ok. I know that slots should be void and should not be used for this, even if they seem to do it.
Emit a signal back seems easy. However, I'd like to know the result immediately. Or should I just call it as regular function instead of using the signal/slot mechanism?
Or is QMetaObject::invokeMethod the correct way?
What's the best solution?
Thanks
McL -
If you want an immediate return value, and if you want the called slot to run in the context of the calling thread, what advantage would you have using signals and slots?
-
@McLion
As @VRonin I'm in guessing mode, but if I understand correctly you just want to sync one of the threads to the other, so this might be what you're after:QMetaObject::invokeMethod(object, "slotName", Qt::BlockingQueuedConnection);
However, I join the fellows that posted before me - please provide a clearer description of the problem.
Kind regards.
-
Thank you guys for the answers.
I think it needs to run in the context of the other thread, not in the context of the calling GUI thread, to have the socket that I am creating and binding run in the other thread.
I'm currently not at my devPC and therefore can not post some code snippets.
I send out messages over this socket that are immediately acknowledged by the remote server and I need to read and check the acks before sending the next message.
I had it all in one (the main GUI) thread before and when sending multiple messages from within one function the responses from the server got read, but only after all messages from the one function had been sent and not in between the messages - due to the eventloop sequence.The idea is to have everything that is going out and comes back in over this socket in a separate thread to make sure it always is serviced in time and not blocked by the main GUI thread.
I already did the same thing with the serial port I'm using and it works perfect. The difference is that I did not need/wanted the immediate confirmation of the successful creation of the new socket after it's initialization with parameters that I receive at runtime and don't have before back in the GUI thread.I hope I could make it somewhat clearer ;-)
Of course, it can be solved without the immediate answer, but since I do not care that the calling thread is blocked for a moment at the time I'm calling the init, this would be a good solution for me.
Thanks, McL -
I tried the invokeMethod .. builds ok, crashes at runtime.
Adding some snippets:
Thread creation:QThread* lolathread = new QThread; LoLaWorker* lolaworker = new LoLaWorker(); lolaworker->moveToThread(lolathread); lolathread->start();
From class LoLaWorker:
public slots: bool InitLoLaUDPsocket(QString port);
and the call:
QMetaObject::invokeMethod(lolaworker, "InitLoLaUDPsocket", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, bInitSuccess), Q_ARG(QString, port));
What am I doing wrong?
Thanks -
@McLion said:
I send out messages over this socket that are immediately acknowledged by the remote server and I need to read and check the acks before sending the next message.
Just queue your outgoing messages. When their time comes, send them from the worker.
The difference is that I did not need/wanted the immediate confirmation of the successful creation of the new socket after it's initialization with parameters that I receive at runtime and don't have before back in the GUI thread.
Usually when writing event-driven, you'd post the request to do something (create socket) and if an error occurs you'd be notified (through a signal for that). One shouldn't go about blocking threads arbitrarily. One of the consequences of using
Qt::BlockingQueuedConnection
is that the caller will hang on the receiver's even loop mutex until the deferred invocation event is processed. This may be very quickly, but depending on the processing the worker does, it may be a very, very long time ...builds ok, crashes at runtime.
Run your program through the debugger. Fix your code! Here's a MWE that illustrates how it works without crashing:
test.h#ifndef TEST_H #define TEST_H #include <QObject> class TestWorker : public QObject { Q_OBJECT public: TestWorker(QObject * = nullptr); public slots: qint32 blockingSlot(); private: qint32 counter; }; #endif // TEST_H
main.cpp
#include <QCoreApplication> #include <QTimer> #include <QThread> #include <QDebug> #include "test.h" TestWorker::TestWorker(QObject * parent) : QObject(parent), counter(0) { } qint32 TestWorker::blockingSlot() { return counter++; } int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); QThread thread; QObject::connect(&app, &QCoreApplication::aboutToQuit, &thread, &QThread::quit); thread.start(); TestWorker worker; worker.moveToThread(&thread); QTimer::singleShot(0, [&worker] () -> void { qint32 i = 5, c; while (i-- > 0) { QMetaObject::invokeMethod(&worker, "blockingSlot", Qt::BlockingQueuedConnection, Q_RETURN_ARG(qint32, c)); qDebug() << c; } }); QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection); int result = QCoreApplication::exec(); thread.wait(); return result; }
As one'd expect the above snippet outputs the numbers from
0
to4
(inclusive) on the standard output (or whatever the debug stream is pointing to).Kind regards.
-
I reworked the whole thing .. thought I'd like to share what I came to ..
I'm now using signals & slots only and it runs in a separate thread. Messages from GUI thread are queued with a signal and conformations from the server are directly handles in the commuication thread - they never need to be passed to the main GUI thread.
Basically works perfectly.
However I still have it sometimes that when a few messages are queued in short succession, some of the messages are sent out in such a short succession that the server has not yet responded - I think I'll add a minimum time gap between successive send message ... -
Hi,
If I understand things correctly, your application should only process the queue when your request has been completed. In that case what about "re-starting" the queue processing when your last request is finished (taking into account both a successful and a failed query)
-
@SGaist
Hi Samuel,
The Server actually is a syslog server only. The messages sent to it are for logging purpose and in it's acknowledge it returns the last internal message number so the device can detect lost messages and react on it as well as log the lost message event to the server in the next message.
It actually works as designed. It's only that I'm sending too fast, before the server did acknowledge and that raises the counter for lost messages. What happens actually confirms the systems function.
I really think I just need to add a minimum interval between sending log messages.
Thanks
Franz -
@McLion said:
I really think I just need to add a minimum interval between sending log messages.
No. That just hides the problem under the carpet, move the program on a slower machine and boom the missed response are back.
- declare a
QQueue<std::tuple<QString/*Method to invoke*/,QString /*Argument to the method*/ > > m_invokeQueue;
andbool m_queueRunning=false;
as private members of LoLaWorker - declare a slot processQueue()
void LoLaWorker::processQueue(){ if (m_invokeQueue.isEmpty()){ m_queueRunning=false; return; } m_queueRunning=true; const auto currentItem = m_invokeQueue.dequeue(); QMetaObject::invokeMethod(lolaworker, std::get<0>(currentItem).toStdString().c_str(), Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, bInitSuccess), Q_ARG(QString, std::get<1>(currentItem))); }
- replace the part where you call invokeMethod with:
m_invokeQueue.enqueue("InitLoLaUDPsocket",port); if(!m_queueRunning) processQueue();
- connect the signal that the server received the message to
processQueue
slot or just call it directly if you don't use signals.
P.S.
I'm 99.9999999% convinced you are doing async communication wrong, if you could post your code we'd probably be able to aim you in the right direction instead of putting band aids to this code - declare a
-
@VRonin
Thanks for your input - there seem to be some misunderstanding.
I can not and do not want to wait for the acknowledge of the server as a trigger to sent the next log message - this is against the idea.
Log messages need always to be sent, regardless wether the server acknowledges or not - imagine the server even does not acknowledge.Every message sent has an ID# that is incremented by one for every new log message sent.
The server acknowledges every message with returning the ID#.Looking at the network of the server with Wireshark and having incoming log messages with time stamps of the same msec - obviously the server has no time to send the acknowledge between incoming protocols. I.e. Sending #1 and # 2 in such a short sequence, obviously when sending #2 the counter for a missed message goes up to one.
This is inherit by this system and is by design.Therefore, I'm going to implement a gap of at least a few msec between sending messages.
Because the messages are all handled and forwarded in the application by signals / QtEvent queues, I dont have a solid idea so far how to do this.
I may have to create a time gated sending queue before handing the messages over to the signal and the QtEvent queue.