Чтобы приостановить рабочий поток, я использовал следующий подход.
Вот часть моего файла GUI.h:
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
Вот часть моего файла GUI.cpp:
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
}
}
Вот мой файл worker.h:
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;
};
Вот часть моего файла worker.cpp:
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();
}
Основная идея заключается в использовании специальных функций QAtomicInt. В рабочем потоке мы проверяем, не изменился ли CHECK. Если это было изменено, мы возвращаемся и ничего не делаем. Чтобы изменить его, мы должны конкурировать с рабочим потоком за доступ к CHECK из потока GUI. Вот почему нам нужно пока блок. Мы вставляем блок while в раздел резюме, хотя в большинстве случаев это будет успешным с первой попытки. Но мы имеем дело с многопоточностью, помните?