I've recently began learning about QThreads and I've a program which runs a 4 hours long loop in a separate thread (so that I may continue to use the GUI). What I am after is, something that will pause/suspend the thread when the user clicks pause qpushbutton, and when the user clicks the resume qpushbutton, the program should resume. How may I achieve this?
I was thinking of sending signals from my main class; however, I'm not sure how I can handle them in the thread. Is it possible to handle signals sent from the main class in a thread? Currently, I have the thread emitting signals to the main class, and that works fine, but I'm not sure how to go about sending threads from the main class, and receiving them in the thread.
Ok, so I suggest you make internal thread variable that will be checked in each step of your loop + QWaitCondition
to resume it.
QWaitCondition
(see Qt docs) to pause thread executionCreate resume method where you will switch off "pause field" and wake QWaitCondition
class MyWorker: public QThread
{
private:
QMutex sync;
QWaitCondition pauseCond;
bool pause;
public:
MyWorker(...): pause(false) {}
void resume()
{
sync.lock();
pause = false;
sync.unlock();
pauseCond.wakeAll();
}
void pause()
{
sync.lock();
pause = true;
sync.unlock();
}
protected:
void run()
{
while(someCondition) // gues it's your loop
{
sync.lock();
if(pause)
pauseCond.wait(&sync); // in this place, your thread will stop to execute until someone calls resume
sync.unlock();
// do your operation
}
}
};
To suspend a working thread I used the following approach.
Here is a part of my GUI.h file:
public:
QAtomicInt check; //it has to be public to be reachable from a
//working thread; we’ll use it as a pause flag
private:
int receiver; //internal flag
QThread *thread; //we will use thread, won’t we?
Worker *worker; //here is where all the work is done
signals:
void begin(); //we will also need a signal
Here is a part of my GUI.cpp file:
Widget::Widget(){
receiver = 0;
check = QAtomicInt(1); //you may use any number, even 42
pb = new QPushButton("Start"); //I used a button to start,
//suspend and resume a working thread
connect(pb, SIGNAL(clicked()), this, SLOT(start()));
thread = new QThread; //who did not read Maya Posch’s blog?
worker = new Worker(this); //we need a pointer to this to reach
//our check flag, remember?
worker->moveToThread(thread);
connect(this, SIGNAL(begin()), worker, SLOT(compute()));
connect(worker, SIGNAL(over()), this, SLOT(ovr()));
thread->start();
}
void Widget::start() {
if ( receiver == 0 ) { //just to remember where we are
pb->setText("Stop");
receiver = 1;
emit begin(); //here we start our heavy job
} else if ( receiver == 1 ) { //here we pause it
pb->setText("Start");
receiver = 2;
while ( !(check.testAndSetOrdered(2, 3))) {}
//this is where all the magic is done testAndSetOrdered
//may fail so we repeat it until it succeeds
} else {
pb->setText("Stop");
receiver = 1;
while ( !(check.testAndSetOrdered(3, 1))) {}
//first we have to restore check to its normal value.
//This time we can almost never fail, but just in case
//I leave the while block here
emit begin(); //here we resume our job
}
}
Here is my worker.h file:
class Worker : public QObject { //do not ask why I did not inherit from QThread,
//just read Maya Posch
Q_OBJECT
public:
Worker(Widget*);
public slots:
void compute(); //the only slot here that does it all
signals:
void over(); //we have to inform the GUI thread that we are over
private:
int limit, counter; //it is important to declare counter
Widget *parent;
};
Here is a part of my worker.cpp file:
Worker::Worker(Widget* par) {
parent = par; //store a pointer to the GUI thread
counter = 1; //it is important to initialize counter HERE
limit = 100000000;
}
void Worker::compute() {
while ( counter < limit ) {
if ( parent->check.testAndSetOrdered(1, 2) ) { //THERE
//testAndSetOrdered may fail, if check was set to another value in the GUI thread.
//If this is the case, we return and DO NOTHING. Compared to techniques with wait()
//and QMutex and QWaitCondition, this approach is easier on CPU.
//do your calculations HERE
counter += 1;
parent->check.testAndSetOrdered(2, 1);
//before the next iteration we have to restore
//check to 1, and we don’t care if we fail here
} else {
return;
}
}
//now we get ready for yet another round of calculations and inform the GUI
//thread that we are over with this round.
counter = 1;
emit over();
}
The basic idea is to use QAtomicInt special features. In the worker thread we check if CHECK is unchanged. If it was changed we return and do nothing. To change it we have to compete with the worker thread for access to CHECK from the GUI thread. That is why we need while block. We put while block in the resume section, though in most cases it will succeed with the first attempt. But we are dealing with multi-threading, remember?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With