Unsolved Correct way to interrupt long running calculation inside a slot in another thread?
-
@CJha
If you are waiting fordoCalculation()
to finish before lettingstopCalculation()
run, it will never abort the loop..... -
@JonB Thanks, but my question is will the call to
stopCalculation()
wait fordoCalculation()
to finish if I am usingQt::QueuedConnection
and calling these slots fromMaster
? -
@CJha no, the slot call would be invoked when the eventloop starts running again, and it won't as long as your function did not return.
The only way would be with a forced direct connection, than the slot is executed in the other thread. But you will have to mutex lock inside the slot than.
-
@J-Hilk Thanks a lot for clarifying this, I working on a solution now but it takes a long time to make the code and I was constantly wondering about this till now.
-
@CJha said in Correct way to interrupt long running calculation inside a slot in another thread?:
std::atomic<bool> abortCalculation
Yes, this should do it
-
Hi everyone, currently I am working on a solution to check which method works best. But since
Master
is a huge class it is taking me some time to finish it for final testing. I have changed my method, now I am using a localQEventLoop
inMaster
to wait for anaborted()
signal from myWorker
object.The code outline for
Master
to controlWorker
is as follows:// Masters slot controlling worker's behaviour // This slot is called by a QPushButton in GUI void Master::startCalculation() { if(workerRunning) // std::atomic<bool> workerRunning; - defined in master.h { // If yes, start an event loop and wait for aborted signal QEventLoop loop; connect(workerObject, &Worker::aborted, &loop, &QEventLoop::quit); workerObject->abortCalculation = true; loop.exec(); } // now start new calculation workerObject->doCalculation(dataVector); // dataVector is QVector<double> }
The
Worker
code is changed now:// a long running calculation inside a slot: void Worker::doCalculation(const QVector<double>& vec) { // Tell master that worker is starting masterObject->workerRunning = true; // Start the calculation part for(int ii = 0; ii < vec.length(); ++ii) { if(abortCalculation){ // std::atomic<bool> abortCalculation; - defined in worker.h emit aborted(); return; } else { // Do the calculation // Each iteration takes around 0.5 ms // Total: 1K to 1000K iteration in each call } } // Tell master worker has stopped masterObject->workerRunning = false; }
To me it appears that the above solution will be the best one. But I still have one small doubt: Does starting a new
QEventLoop
inside a slot of main GUI thread and waiting for it toquit()
stop the event loop of main GUI thread? As far as my understanding goes, I think that will be the case, if so, is there a way I can wait foraborted()
signal inside a main GUI slot without blocking the entire main GUI thread? -
@CJha
IfworkerRunning
is true yourstartCalculation()
blocks and waits untilWorker
is aborted, and only then sets offdoCalculation()
. And ifWorker
does not callaborted()
that will be when Hell freezes over. Is this really your intended architecture?? I'm lost with what you are up to.... -
@JonB Thanks, yes this is my intended architecture. In my
Worker
I am generating a line plot on aQImage
which it sends toMaster
. TheMaster
is a subclassedQWidget
where I display thisQImage
as a plot in a certain area. My architecture is this way because I have enabled multiple interactions with the plot inMaster
using the mouse and keyboard interactions, such that users can zoom and scroll. However, since the data to be plotted is quite big I need to have a way to abort the generation ofQImage
insideWorker
otherwise the zoom and scroll behaviour will lag enormously. -
@CJha
Doesn't sound right to me. If yourWorker
finishes without goingemit aborted();
you never exit yourloop.exec()
and are stuck there, like I said, till The Universe comes to an end.I don't understand, so I will just say this. I have a funny feeling that what you want is for the main thread to check for worker aborted intermittently, while remaining UI-responsive and checking for aborted, which requires signal processing. If that is so you have two possible approaches:
- Call
processEvents()
each time round its loop. Signals will be processed at that point. - Change so you do not have a tight loop around each element in the vector. Instead have a
QTimer()
and on each timeout process some number of the remaining items in the vector, noting where you got to.
I leave it to you....
- Call
-
You can use your first example, an atomic bool and check this in every n'th iteration. Everything else is not needed and will not help.
You can also avoid the atomic bool and use the QThread built-in QThread::requestInterruption() and check for QThread::isInterruptionRequested() once in a while in your loop.