I am working on a plotting algorithm. To do this I get the data from a DAQ board in my main GUI thread and then I send the data over to a worker thread to be processed, the worker thread emits a signal with the new QImage
which I display as a plot in my GUI. The problem is the function, let's call it generateImage()
, to calculate and generate the QImage
takes a long time (~50-70 milliseconds, depending on data length) and in between this time another set of data might arrive which will require the worker thread to recalculate the plot from the beginning. I want the generateImage()
to abandon the calculation and restart from the beginning if the new data arrives while it is still calculating. My approach is to set a member boolean variable, let's call it b_abort_
, and check if it is set to true inside generateImage()
and return
if it's true, outside generateImage()
it always remains true and I only set it to false before generateImage()
is called.
All this happens in a worker thread, I subclass QObject
and use moveToThread()
to move it to a worker thread.
The function which starts calculation:
void WorkerThread::startCalc()
{
b_abort_ = false;
generateImage();
// if b_abort_ is set to true, generateImage() will return prematurely
if(b_abort_)
emit calcFinished();
else
b_abort_ = true;
}
Function which does all calculations and generates image:
void WorkerThread::generateImage()
{
/* Calculation of some parts */
for(int ii = 0; ii < Rawdata.length(); ++ii) // Starting main time consuming loop
{
if(b_abort_)
return;
/* Perform calculation for one data point from Rawdata */
}
// data calculation complete, now it's time to generate QImage
// before that I check if b_abort_ is set to true
if(b_abort_)
return;
for(int ii = 0; ii < CalculatedData.length(); ++ii) // plotting calculated data on QImage
{
if(b_abort_)
return;
/* plot one data point from CalculatedData vector */
}
// generation of QImage finished, time to send the signal
emit renderedPlot(image); // image is a QImage object
}
In my worker thread, I have a slot to receive data from the main GUI Thread, it is configured with Qt::QueuedConnection
(the default) as the connection type:
void WorkerThread::receiveData(QVector<double> data)
{
if(!b_abort_) // check if calculation is still running
{
QEventLoop loop;
connect(this, &WorkerThread::calcFinished, &loop, &QEventLoop::quit);
b_abort_ = true; // set it to true and wait for calculation to stop
loop.exec();
// start new calculation
RawData = data;
startClac();
}
else
{
RawData = data;
startClac();
}
}
When I use this approach in my main GUI Thread, the generateImage()
function blocks all event loops, and my GUI freezes, which makes me think that inside a single thread (main GUI thread or a worker thread) only one function can run at a time and so any change in b_abort_
is not applied until the thread's event loop returns to process other functions. When using WorkerThread
it is difficult to verify if this is working, some times it works fine while other times it generates bad allocation error which seems like it is not working (although it might be because of a different reason entirely, I am not sure). I would like to ask your opinion, is this the right approach to stop a long-running calculation prematurely? Are there any other methods that I can use which will be more robust than my current approach?