Nested thread (QThread) does not emit the signal
-
Hi all,
This is my code in the QtWidgetsApplication1.cpp (main file, main thread) where I'm creating a new worker class object and starting the thread:
void QtWidgetsApplication1::on_pushButton_4_clicked() { ui.pushButton_2->setEnabled(false); mThread = new QThread; worker = new BinconThreadWorker; worker->Stop = false; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process2())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(process3_Fb(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(valueSecChanged(std::string)), this, SLOT(onValueSecChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); }
So I'm starting in the BinconThreadWorker process2:
void BinconThreadWorker::process2() { //(...) if (Taken==false) { mThread = new QThread; worker = new BinconThreadWorker_child0; Taken = true; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process3())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), this, SLOT(threadTaken())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); } //(...) } void BinconThreadWorker::onValueChanged(std::string s) { emit valueChanged(s); }
and here I'm starting nested thread here and process3:
void BinconThreadWorker_child0::process3() { std::string s = "Started"; emit valueChanged(s); emit finished(); }
For now I expect it to work that way:
- I'm creating from the main thread new BinconThreadWorker and starting it in the new QThread.
- BinconThreadWorker function process2 is creating new, nested BinconThreadWorker_child0 and starting next new QThread.
- From the nested BinconThreadWorker_child0 and it's process3 function I'm sending valueChanged signal and it should go to onValueChanged slot in the BinconThreadWorker and then emit signal to the main thread.
Is it the correct way of doing this? As I'm not getting emitted string at all or the program freezes and I'm getting "ucrtbase.pdb not loaded". Signals from the BinconThreadWorker are emitted correctly.
I'm using MS Visual Studio 2022 and QT 6.2.3. Thanks for all the tips.
-
Hi all,
This is my code in the QtWidgetsApplication1.cpp (main file, main thread) where I'm creating a new worker class object and starting the thread:
void QtWidgetsApplication1::on_pushButton_4_clicked() { ui.pushButton_2->setEnabled(false); mThread = new QThread; worker = new BinconThreadWorker; worker->Stop = false; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process2())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(process3_Fb(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(valueSecChanged(std::string)), this, SLOT(onValueSecChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); }
So I'm starting in the BinconThreadWorker process2:
void BinconThreadWorker::process2() { //(...) if (Taken==false) { mThread = new QThread; worker = new BinconThreadWorker_child0; Taken = true; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process3())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), this, SLOT(threadTaken())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); } //(...) } void BinconThreadWorker::onValueChanged(std::string s) { emit valueChanged(s); }
and here I'm starting nested thread here and process3:
void BinconThreadWorker_child0::process3() { std::string s = "Started"; emit valueChanged(s); emit finished(); }
For now I expect it to work that way:
- I'm creating from the main thread new BinconThreadWorker and starting it in the new QThread.
- BinconThreadWorker function process2 is creating new, nested BinconThreadWorker_child0 and starting next new QThread.
- From the nested BinconThreadWorker_child0 and it's process3 function I'm sending valueChanged signal and it should go to onValueChanged slot in the BinconThreadWorker and then emit signal to the main thread.
Is it the correct way of doing this? As I'm not getting emitted string at all or the program freezes and I'm getting "ucrtbase.pdb not loaded". Signals from the BinconThreadWorker are emitted correctly.
I'm using MS Visual Studio 2022 and QT 6.2.3. Thanks for all the tips.
@Jacobotto said in Nested thread (QThread) does not emit the signal:
Is it the correct way of doing this?
Cant see something wrong here. But cant tell...At least not from what you've posted (we cant see how your classes look like).
Made a simple "3-cascading worker thread example" just to be sure, that I'm not missing anything and it works for me... it was possible to pass the string through all workers and end theQThreads
properly.QThread(0x1a3325e0) "Worker started" // last child worker QThread(0x1a3325d0) "Worker started" // first worker QThread(0x18ae79c8) "Worker started" // "main" gui thread with mainWindow
All threads receive the string as you can see from the output in
valueChanged
slot of every worker.So what else do you do in/with your classes? Show them please.
You could reduce your code to a minimal example (like I created for testing) and check if the error still occures... but the code, from Qt / signal-slot perspective, looks ok. -
Hi all,
This is my code in the QtWidgetsApplication1.cpp (main file, main thread) where I'm creating a new worker class object and starting the thread:
void QtWidgetsApplication1::on_pushButton_4_clicked() { ui.pushButton_2->setEnabled(false); mThread = new QThread; worker = new BinconThreadWorker; worker->Stop = false; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process2())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(process3_Fb(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(valueSecChanged(std::string)), this, SLOT(onValueSecChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); }
So I'm starting in the BinconThreadWorker process2:
void BinconThreadWorker::process2() { //(...) if (Taken==false) { mThread = new QThread; worker = new BinconThreadWorker_child0; Taken = true; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process3())); QObject::connect(worker, SIGNAL(valueChanged(std::string)), this, SLOT(onValueChanged(std::string))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), this, SLOT(threadTaken())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); } //(...) } void BinconThreadWorker::onValueChanged(std::string s) { emit valueChanged(s); }
and here I'm starting nested thread here and process3:
void BinconThreadWorker_child0::process3() { std::string s = "Started"; emit valueChanged(s); emit finished(); }
For now I expect it to work that way:
- I'm creating from the main thread new BinconThreadWorker and starting it in the new QThread.
- BinconThreadWorker function process2 is creating new, nested BinconThreadWorker_child0 and starting next new QThread.
- From the nested BinconThreadWorker_child0 and it's process3 function I'm sending valueChanged signal and it should go to onValueChanged slot in the BinconThreadWorker and then emit signal to the main thread.
Is it the correct way of doing this? As I'm not getting emitted string at all or the program freezes and I'm getting "ucrtbase.pdb not loaded". Signals from the BinconThreadWorker are emitted correctly.
I'm using MS Visual Studio 2022 and QT 6.2.3. Thanks for all the tips.
@Jacobotto said in Nested thread (QThread) does not emit the signal:
As I'm not getting emitted string
You should take a look at the debug output - I'm pretty sure Qt complains that std::string is not a registered metatype (and also tells you what to do in this case whereas I simply would switch to QString or QByteArray instead)
-
I changed to QString but it didn't help. Anyway, I commented launching thread section in BinconThreadWorker and still I'm getting an error, but not always, just like before. So to explain more this is the while loop from process2 which is running when error happens:
while (true) { if (this->Stop == true) break; j = bincon_obj.get24hTicker(); int v_it = 0; for (auto it : j) { symbol = it.at("symbol").dump(); symbol = symbol.substr(1, symbol.length() - 2); if (symbol.substr(symbol.length() - 4, 4) != "USDT") continue; if (symbol != v[v_it][0]) { s = "Incompatibility of vector and data, breaking..."; logFileStream << s.toStdString() << "\n"; logFileStream.flush(); emit valueChanged(s + "\n"); break; this->Stop = true; } lastPrice = it.at("lastPrice").dump(); lastPrice = lastPrice.substr(1, lastPrice.length() - 2); flastPrice = stof(lastPrice); //(...) if (v[v_it][2] == "8") { s = "Worth interest: " + QString::fromStdString(v[v_it][0]); logFileStream << s.toStdString() << "\n"; logFileStream.flush(); emit valueChanged(s); /* if (Taken == false) { mThread = new QThread; worker = new BinconThreadWorker_child0; Taken = true; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process3())); QObject::connect(worker, SIGNAL(valueChanged(QString)), this, SLOT(onValueChanged(QString))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), this, SLOT(threadTaken())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); }*/ } v_it++; if (this->Stop == true) break; }
My program connects with API and gets some data, then it compares it and emits signals if requirments are met.
In the first post I showed you how I'm starting QThread from the main GUI thread and this is the short code of final slot:void QtWidgetsApplication1::onValueChanged(QString s) { ui.plainTextEdit->appendPlainText(s); ui.plainTextEdit->verticalScrollBar()->setValue(maximumHeight()); }
So as you can see, I have plaintTextEdit widget which is my "console" where all QString data should be shown and indeed it normally works until some time when I get mentioned error:
-
I changed to QString but it didn't help. Anyway, I commented launching thread section in BinconThreadWorker and still I'm getting an error, but not always, just like before. So to explain more this is the while loop from process2 which is running when error happens:
while (true) { if (this->Stop == true) break; j = bincon_obj.get24hTicker(); int v_it = 0; for (auto it : j) { symbol = it.at("symbol").dump(); symbol = symbol.substr(1, symbol.length() - 2); if (symbol.substr(symbol.length() - 4, 4) != "USDT") continue; if (symbol != v[v_it][0]) { s = "Incompatibility of vector and data, breaking..."; logFileStream << s.toStdString() << "\n"; logFileStream.flush(); emit valueChanged(s + "\n"); break; this->Stop = true; } lastPrice = it.at("lastPrice").dump(); lastPrice = lastPrice.substr(1, lastPrice.length() - 2); flastPrice = stof(lastPrice); //(...) if (v[v_it][2] == "8") { s = "Worth interest: " + QString::fromStdString(v[v_it][0]); logFileStream << s.toStdString() << "\n"; logFileStream.flush(); emit valueChanged(s); /* if (Taken == false) { mThread = new QThread; worker = new BinconThreadWorker_child0; Taken = true; QObject::connect(mThread, SIGNAL(started()), worker, SLOT(process3())); QObject::connect(worker, SIGNAL(valueChanged(QString)), this, SLOT(onValueChanged(QString))); QObject::connect(worker, SIGNAL(finished()), mThread, SLOT(quit())); QObject::connect(worker, SIGNAL(finished()), this, SLOT(threadTaken())); QObject::connect(worker, SIGNAL(finished()), worker, SLOT(deleteLater())); QObject::connect(mThread, SIGNAL(finished()), mThread, SLOT(deleteLater())); worker->moveToThread(mThread); mThread->start(); }*/ } v_it++; if (this->Stop == true) break; }
My program connects with API and gets some data, then it compares it and emits signals if requirments are met.
In the first post I showed you how I'm starting QThread from the main GUI thread and this is the short code of final slot:void QtWidgetsApplication1::onValueChanged(QString s) { ui.plainTextEdit->appendPlainText(s); ui.plainTextEdit->verticalScrollBar()->setValue(maximumHeight()); }
So as you can see, I have plaintTextEdit widget which is my "console" where all QString data should be shown and indeed it normally works until some time when I get mentioned error:
@Jacobotto said in Nested thread (QThread) does not emit the signal:
while (true) {
if (this->Stop == true) break; j = bincon_obj.get24hTicker(); int v_it = 0; for (auto it : j) { symbol = it.at("symbol").dump(); symbol = symbol.substr(1, symbol.length() - 2); if (symbol.substr(symbol.length() - 4, 4) != "USDT") continue; if (symbol != v[v_it][0]) { s = "Incompatibility of vector and data, breaking..."; logFileStream << s.toStdString() << "\n"; logFileStream.flush(); emit valueChanged(s + "\n");
What do you think how emitting/receiving a signal inside a
while(true)
loop should work?!As long as you are trapped inside this while - loop, you wont be able to receive signals from your other workers. That's why you see the
valueChanged
response not frequently (or never).Check out this post on StackOverflow... in the comments, you also find a possible solution.
-
Ok I think I understand in case why I can't get anything from nested thread to the parent nested thread. However my direct signals from BinconThreadWorker to main GUI thread work fine until the error. I think that way is correct, like in this example: GuiThread.
void MyThread::run() { for(int i = 0; i <= 100; i++) { QMutex mutex; // prevent other threads from changing the "Stop" value mutex.lock(); if(this->Stop) break; mutex.unlock(); // emit the signal for the count label emit valueChanged(i); // slowdown the count change, msec this->msleep(500); } }
The author is emitting signals while the for loop is running, so similar like in my example.
In my program when the other void (process) is started from the main GUI thread it is also running in while(true) loop without any issues (or I didn't met it already). If I'm starting some function from the GUI I need to make it run until user clicks on "Stop" button, so I need it to constantly connect with the API and if some requirments are met it should start next, nested thread to do something because it needs to run further itself and do checks. How can I solve this, looking from the C++ perepective, not QT (like QEventLoop)? What can be the alternative? When reading the stackoverflow topic @Pl45m4 sent I think it should be based on signals/slots instead. Maybe something like this?:void BinconThreadWorker::process2() { //do something but not while(true) emit checkIfStop(); } checkIfStopFunc(){ if(this->Stop==true){ emit finished(); }else{ process2(); } }
Anyway, it won't solve the issue with ucrtbase.dll.
-
Ok I think I understand in case why I can't get anything from nested thread to the parent nested thread. However my direct signals from BinconThreadWorker to main GUI thread work fine until the error. I think that way is correct, like in this example: GuiThread.
void MyThread::run() { for(int i = 0; i <= 100; i++) { QMutex mutex; // prevent other threads from changing the "Stop" value mutex.lock(); if(this->Stop) break; mutex.unlock(); // emit the signal for the count label emit valueChanged(i); // slowdown the count change, msec this->msleep(500); } }
The author is emitting signals while the for loop is running, so similar like in my example.
In my program when the other void (process) is started from the main GUI thread it is also running in while(true) loop without any issues (or I didn't met it already). If I'm starting some function from the GUI I need to make it run until user clicks on "Stop" button, so I need it to constantly connect with the API and if some requirments are met it should start next, nested thread to do something because it needs to run further itself and do checks. How can I solve this, looking from the C++ perepective, not QT (like QEventLoop)? What can be the alternative? When reading the stackoverflow topic @Pl45m4 sent I think it should be based on signals/slots instead. Maybe something like this?:void BinconThreadWorker::process2() { //do something but not while(true) emit checkIfStop(); } checkIfStopFunc(){ if(this->Stop==true){ emit finished(); }else{ process2(); } }
Anyway, it won't solve the issue with ucrtbase.dll.
@Jacobotto said in Nested thread (QThread) does not emit the signal:
The author is emitting signals while the for loop is running, so similar like in my example.
Not every tutorial or guide you find out there is recommended or correct...
I suggest you to read
- https://doc.qt.io/qt-5/threads-technologies.html
(esp. this chapter, last two rows of the table since you have/want a permanent call)
and
There you can compare what possibilities you have and decide what might fit the most.
But in general: Mixing infinite loops and signal-slots is not the best approach, since you are blocking the event loop. Incoming signals and events won't get processed until you break out of your while loop... and then your whole "task" might be over. So you won't receive any signals at runtime.If you want to stick to the infinite loop, you should use the
QThread::run
approach (override it) and "interrupt" yourrun
usingQThread::requestInterruption
when you want to stop it. The disadvantage is that this lacks the easy communication with other threads using signals and slots (at least receiving data in between is not possible; your threads need to send and receive as your project is now) - https://doc.qt.io/qt-5/threads-technologies.html
-
Thank you, it explains a lot. So all my threads just do some work and sending info back to the main GUI thread, they don't have to receive from the parent thread additional data besides termination. I could indeed use QThread::run but it would be best that nested thread would send signal directly to GUI thread, omitting parent thread (in this case the one handling BinconThreadWorker class object). Is it possible?
Anyway, when leaving just main thread and new one started from there, like I wrote before, another very similar function with while(true) loop is working completely fine and this one too (because nothing is blocking receiving the signals in the main thread), until ucrtbase.dll error. Any thoughts? -
Thank you, it explains a lot. So all my threads just do some work and sending info back to the main GUI thread, they don't have to receive from the parent thread additional data besides termination. I could indeed use QThread::run but it would be best that nested thread would send signal directly to GUI thread, omitting parent thread (in this case the one handling BinconThreadWorker class object). Is it possible?
Anyway, when leaving just main thread and new one started from there, like I wrote before, another very similar function with while(true) loop is working completely fine and this one too (because nothing is blocking receiving the signals in the main thread), until ucrtbase.dll error. Any thoughts?FYI I have copied my inifinity loop to the c++ console app and it's crashing randomly too due to json library, so it's not the QT issue, I suspected that. However, the topic problem was solved I suppose anyway, I will try to use QThread::Run, or try to send signal from nested thread directly to GUI thread if it's possible (please answer if anybody knows) or just I will go not the way Main Thread->start Thread1-> start Thread2 but Main Thread->Thread1->emit signal to Main Thread to start Thread2. It will fit my needs too.