Passing classes to worker in QThread - c++

I'm developing an application which parses a list of binary files and saves the results into a database. The processing of the files takes a rather long time. Therefore I'm showing the user a progress bar and a list of processed files. When a file is successfully processed, I want to update the processbar. If the user aborts the processing (i.e. clicks on the window's 'x' -> &ParseAssistant::rejected), I need to kill the thread and delete the allocated objects (i.e. PluginWorker* and QThread*).
/* mainwindow.cpp */
ParseAssistant parseWindow(files.size());
QThread* thread = new QThread();
PluginWorker* worker = new PluginWorker(pluginConnector, pluginProcessor, files);
worker->moveToThread(thread);
// Update GUI (progressbar + filelist)
connect(worker, &PluginWorker::updateProgress, &parseWindow, &ParseAssistant::updateProgress);
connect(thread, SIGNAL (started()), worker, SLOT (process()));
connect(worker, SIGNAL (finished()), thread, SLOT (quit()));
connect(thread, SIGNAL (finished()), thread, SLOT (deleteLater()));
connect(worker, SIGNAL (finished()), worker, SLOT (deleteLater()));
// Stop PluginWorker on 'x'-click
connect(&parseWindow, &ParseAssistant::rejected, worker, &PluginWorker::stop);
connect(worker, &PluginWorker::finished, &parseWindow, &ParseAssistant::parsingFinished);
thread->start();
parseWindow.exec();
First, I'm creating a QDialog (ParseAssistant) which contains the progressbar and the list of processed files. Afterwards I create a Worker-Thread-pair and bind them as described in 'How To Really, Truly Use QThreads'. The 'pluginConnector' has access to the plugin an parses the files; the pluginProcessor writes the results to a database.
/* pluginworker.cpp */
PluginWorker::PluginWorker(PluginConnector& pc, PluginProcessor& processor, QStringList list)
{
connect(this, SIGNAL(parseNext(int)), this, SLOT(process(int)));
}
// Called on 'x'-press
void PluginWorker::stop()
{
isStopped = true;
}
void PluginWorker::process(int idx)
{
if (isStopped) {
emit finished();
return;
}
if (0 <= idx && idx < files.length()) {
/*
* Parse file using PluginConnector and
* use result with PluginProcessor
*/
emit updateProgress(idx+1, error, source);
emit parseNext(idx+1);
} else {
emit finished();
}
}
I use signals/slots to call parseNext, because this allows Qt to call stop() (when &ParseAssistant::rejected signal is received). An alternative would be to iterate over the files and call QCoreApplication::processEvents to process signals.
This construct mostly works, but crashes when I abort the process using the 'x'. I can't track down exactly what crashes the application, but I'm fairly certain that my approach is flawed, because the 'worker' and the 'pluginConnector/pluginProcessor' live in different threads, but I don't know how to correctly use this.

Related

GUI app frozen while process and generate a file

I have a QT GUI application in c++ that aims to process and generates binary files.
The App works fine but it looks like it's frozen when it enters the while loop of processing and writing in the file.
I solve this by coping qApp->processEvents(); into the while loop. but the problem is that it takes much more time for generating a file:
without qApp->processEvents(); in the loop => it takes 4second
with qApp->processEvents(); in the loop => it takes 50 second for exact the same file
for (unsigned long int k=0; k<DATASIZE ; k++){
qApp->processEvents();
/* Some process*/
DataToFile.push_back(Process_Function(DATA));
}
/*Generating file*/
myFile.write((char *)&DataToFile[0], DataToFile.size()*sizeof (float));
DATA SIZE around a couple of millions
Process_Function: take specific data, calculate the value and return it back.
Questions:
1- Is there a way to process the data, generate files without being frozen and without the huge delay of the qApp->processEvents();
2- Is it possible to run qApp->processEvents(); in another thread? / OR is there another way to do it?
First, create an object that encapsulates your work:
class Generator : public QObject {
Q_OBJECT
signals:
void progress(float);
void done();
slots:
void doWork() {
QVector<float> DataToFile;
for (unsigned long int k=0; k<DATASIZE ; k++){
/* Some process*/
DataToFile.push_back(Process_Function(DATA));
if (k % 100 == 0) { // Inform the UI thread every 100 datapoints
emit progress(k/DATASIZE);
}
}
myFile.write((char *)&DataToFile[0], DataToFile.size()*sizeof (float));
emit done();
}
};
Then create a new thread and have the object do its work there:
QThread *t = new QThread(this);
Generator *g = new Generator;
g->moveToThread(t);
QObject::connect(t, &QThread::started, g, &Generator::doWork);
QObject::connect(g, &Generator::done, t, &QThread::quit);
QObject::connect(t, &QThread::finished, g, &QObject::deleteLater);
QObject::connect(t, &QThread::finished, t, &QThread::deleteLater);
t->start();
The first two connect statements tie the lifetime of the generator to that of the thread, the last two clean up everything once the thread exits.
And you can of course connect to the Generator::progress signal to monitor the generation progress.

Qt, C++, How to Quit QThread

I have a calculator and a calculator method startCalculations() which is to put onto a QThread. I successfully connect mStopCalcButton and the thread's quit()/terminate(). However, when I press mStopCalcButton, the thread does not quit/terminate.
Here is the code in question...
mStopCalcButton->setEnabled(true);
QThread* thread = new QThread;
Calculator* calculator = new Calculator();
calculator->moveToThread(thread);
connect(thread, SIGNAL(started()), calculator, SLOT(startCalculations())); //when thread starts, call startCalcuations
connect(calculator, SIGNAL(finished()), thread, SLOT(quit()));
connect(calculator, SIGNAL(finished()), calculator, SLOT(deleteLater()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
thread->start();
connect(mStopCalcButton, SIGNAL(released()), thread, SLOT(quit()) );
In the calculator, this is the only defined method...
void Calculator::startCalcuations()
{
int x = 0;
while (true)
qDebug() << x++;
}
Why does my QThread not quit?
The first thing, function QThread::quit() only tell that thread to exit it's event loop, but do nothing related to terminate or exit. you can read Qt document here: QThread:quit()
To terminate a thread, in general implement, you should change your thread's running function code by using stop flag rather than infinitive loop. Whenever you want to terminate thread, you only need change that stop flag and wait for thread terminating.
Using stop flag:
void Calculator::startCalcuations()
{
int x = 0;
while (!mStopFlag) {
qDebug() << x++;
// In addition, you should add a little sleep here to avoid CPU overhelming
// like as msleep(100);
}
}
Terminate thread by turning on the stop flag:
void YourClass::requestTerminateThread()
{
mStopFlag = true;
if(!thread.wait(500))
{
thread.terminate(); // tell OS to terminate thread
thread.wait(); // because thread may not be immediately terminated by OS policies
}
}
In addition, as you can see my comment on above code, you should add some thread sleep time to avoid CPU overhelming.
For more information, please clearly read QThread document specs first.

QThread stuck in second run

I have a QThread on my embedded device. every time I run the application my thread stuck after second run. I try to kill my thread after first run. Still device stuck after second run. I couldn't run my thread correctly.
Here is my code;
void ThreadCurrency::run()
{
QMutex mutex;
mutex.lock();
if(this->CurrencyStop == true)
{
mutex.unlock();
return;
}
QByteArray strdata;
// Create QProcess object
processCurrency = new QProcess();
processCurrency->start("curl --insecure -v --cacert /data/ca/cert.pem https://secure.*******************/fx.jsp");
if (processCurrency->waitForStarted(-1))
{
while(processCurrency->waitForReadyRead(-1))
{
strdata += processCurrency->readAllStandardOutput();
}
QMessageBox msgBox1;
msgBox1.setWindowTitle("eCode Read");
msgBox1.setText(strdata);
msgBox1.exec();
}
else
{
while(processCurrency->waitForReadyRead(-1))
{
strdata += processCurrency->readAllStandardError();
}
QMessageBox msgBox1;
msgBox1.setWindowTitle("eCode Error");
msgBox1.setText(strdata);
msgBox1.exec();
}
mutex.unlock();
sleep(1);
//*****************************************************************
emit CurrencyChanged(aGBP, aEUR, aUSD, sGBP, sEUR, sUSD);
}
** The Output shows in a json format:**
{
"date": "20171107", "currency": {
"dolar": {
"buy": "3,8200",
"sale": "3,9050",
"e_sale": "3,8200"
},
}
}
Thank you for suggestion. The QMutexLocker can’t work in my embedded device. QMutex and the QThread is the closes I can get in my device.
My problem is the below line: emit CurrencyChanged(aGBP, aEUR, aUSD, sGBP, sEUR, sUSD); The line runs but It doesn’t fire onCurrencyChanged SLOT. Anything in onCurrencyChanged doesn’t run. My main thread code is:
currencyThread = new ThreadCurrency (this);
connect(currencyThread,SIGNAL(CurrencyChanged(QString, QString, QString, QString, QString, QString)), this, SLOT(onCurrencyChanged (QString, QString, QString, QString, QString, QString)));
currencyThread->CurrencyStop = false;
currencyTimer = new QTimer(this);
connect(currencyTimer, SIGNAL(timeout()),this, SLOT(showCurrencyStatus()));
currencyTimer->start(30000);
void MainWindow::onCurrencyChanged(QString aGBP, QString aEUR, QString aUSD, QString sGBP, QString sEUR, QString sUSD)
{
// SHOW Currency
ui->lblALIS_STG->setText(aGBP);
ui->lblALIS_EUR->setText(aEUR);
ui->lblALIS_USD->setText(aUSD);
QCoreApplication::processEvents();
}
1) You are not allowed to access Widgets in a thread besides the main thread.
Thus move all QMessageBox code to the main thread (e.g. after receiving the CurrencyChanged signal).
2) Make sure, that the CurrencyChanged signal is connected using a QueuedConnection, otherwise, your GUI will be executed inside the secondary worker thread.
3) Do not use QMutex directly, use a QMutexLocker instead
4) Why do you need the Mutex anyhow? Locking based on (arbitrary) input thus arbitrary time is close to deadlocking your application.

How do I wait for a thread to finish in Qt without blocking its execution?

I have a function that creates a bunch of QThreads to speed up a long calculation, but when I try and sleep until the calculation is done, the background threads never execute for some reason.
I can't figure out why the threads don't execute after I call thread->start(). Here's my code:
multithread.run_multithread(run_function, thread_count);
while(multithread.running)
QThread::msleep(100);
qDebug() << "done"; // Never reaches this point
And the multithread function:
void Multithread::run_multithread(std::function<void
(int)>run_function_in, int thread_count) {
running = true;
// Create workers and threads
int thread_idx;
for(thread_idx=0; thread_idx<thread_count; thread_idx++) {
ThreadWorker *worker = new ThreadWorker(this, run_function_in);
QThread *thread = new QThread();
// Set workers and threads to delete when done
worker->moveToThread(thread);
connect(worker, &ThreadWorker::finished, this, &Multithread::check_progress);
connect(thread, &QThread::started, worker, &ThreadWorker::run);
connect(worker, &ThreadWorker::finished, thread, &QThread::quit);
connect(worker, &ThreadWorker::finished, worker, &ThreadWorker::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// Start thread
thread->start();
}
}
running gets set to false in Multithread::check_progress when all the threads finish their part of the calculation. When I remove QThread::msleep(100) the calculation will execute normally, but I need some way to block until it completes.
Rather than using QThread, I would recommend using the Qt Concurrent functionality which provides a higher level of abstraction over the thread primitives. You can use QFutureSynchronizer to wait for several concurrent futures to finish. This object will wait for all attached futures to finish before it allows itself to be destroyed.
An example of how to start several threads and then wait for them all to finish is:
#include <QtConcurrent>
void run_multithread(...) {
// Set up a new synchronizer.
QFutureSynchronizer<void> synchronizer;
// Run n tasks in parallel.
for (int thread_idx = 0; thread_idx < thread_count; ++thread_idx) {
synchronizer.addFuture(QtConcurrent::run(run_function_in));
}
// The synchroniser will wait for all threads to finish before returning.
}
It could be because of optimization, since multithread.running is not changed inside the loop. However, here the main reproducible reason is that the slot Multithread::check_progress is never called by ThreadWorker::finished signals, since the instance of Multithread belongs to your current thread, but the event loop is blocked.
Instead of such sleep you can emit a signal from Multithread object when all threads are finished. Then the computation may be continued from a slot connected to that signal.

Qt emitted signal form a background thread to the main thread never arrives

I have difficulties to get a Qt::QueuedConnection between a background thread and my main application running. I have a camera capturing class derived from QObject which is moved to a QThread by using:
m_CameraCapture.moveToThread(&m_CameraCaptureThread);
Afterwards, I connect the signals and slots:
//Connect error signal)
QObject::connect(&m_CameraCapture, SIGNAL(error(QString,QString)), this, SLOT(reportError(QString,QString)));
//Connect the finished signal of the worker class to the thread for quitting the loop
connect(&m_CameraCapture, SIGNAL(finished()), &m_CameraCaptureThread, SLOT(quit()));
//This connections guarantees that the *m_CVideoCapture is automatically deleted if the event loop of the thread is terminated. Therefore, m_CVideoCapture does not need to be released manually if the capturing process is stopped.
QObject::connect(&m_CameraCaptureThread, SIGNAL(finished()), &m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(&m_CameraCapture, SIGNAL(finished()), &m_CameraCapture, SLOT(deleteLater()));
//Connect sendFrame to update frame for displaying the current frame
QObject::connect(&m_CameraCapture, SIGNAL(sendFrame(cv::Mat)), this, SLOT(receiveFrame(cv::Mat)),Qt::BlockingQueuedConnection);
QObject::connect(this, SIGNAL(startGrabbing()), &m_CameraCapture, SLOT(startGrabbing()));
QObject::connect(this, SIGNAL(stopGrabbing()), &m_CameraCapture, SLOT(stopGrabbing()));
The m_CameraCapture object contains a timer which calls in regular intervals the grabFrame() slot of m_CameraCapture. The function is defined by:
void CCameraCapture::grabFrame(){
QMutexLocker ml(&m_Mutex);
qDebug() << "Elapsed time " << GetTickCount()-lastTickCount<<" ms";
qDebug() << "Thread ID timer: " << m_Timer.thread();
qDebug() << "Thread ID worker: " << this->thread();
lastTickCount=GetTickCount();
//Local image storage
cv::Mat cvFrameBGR;
//Get new frame from camera
m_Cap>>cvFrameBGR;
//cv::Mat cvFrameRGB;
////Convert frame to RGB
//cv::cvtColor(cvFrameBGR, cvFrameRGB, CV_BGR2RGB);
////Convert cv::Mat to QImage
//m_Frame=QImage((uchar*)(cvFrameRGB.data),cvFrameRGB.cols,cvFrameRGB.rows,QImage::Format_RGB888);
//Send frame to receivers
emit sendFrame(cvFrameBGR);
}
The thread IDs of timer and thread are the same and different to the thread ID of the main application - so this seems to be correct. The point is the line
emit sendFrame(cvFrameBGR);
This signal only arrives at the main application if I use
QObject::connect(&m_CameraCapture, SIGNAL(sendFrame(cv::Mat)), this, SLOT(receiveFrame(cv::Mat)),Qt::BlockingQueuedConnection);
in the main application to connect it. But this is not what I want because it will slow down my capturing loop. I would like to connect it with a Qt::QueuedConnection. However, when I use a QueuedConnection instead of the BlockingQueuedConnection the receiveFrame(cv::Mat) slot in the receiving Object is never executed (Also with AutoConnection it wont work). The receiving Object is a cameraHandler class which is derived from QObject. Thanks for any hint!