В настоящее время мне известны три альтернативных решения.
Согласно QThread::isInterruptionRequested
:
Старайтесь не называть это слишком часто, чтобы снизить накладные расходы.
Принимая во внимание, что QCoreApplication::processEvents
не делает никаких замечаний по поводу производительности или использования памяти, поэтому я не думаю, что QThread::requestInterruption
является улучшением по сравнению с QCoreApplication::processEvents
в этом случае.
Основная характеристика атомарных объектов состоит в том, что доступ к этому содержавшемуся значению из разных потоков не может вызывать гонки данных [...]
Логическое значение может храниться в std::atomic
, который можно сделать членом класса Controller
вместо класса Worker
.Затем нам нужно передать ссылку на aborted
и сохранить ее в Worker
, а при необходимости установить true
из Controller
.
Я не полностью протестировал этот подход, поэтомуПожалуйста, поправьте меня, если я что-то не так понял.
class Worker : public QObject {
Q_OBJECT
std::atomic<bool> &aborted;
public:
Worker(std::atomic<bool> &aborted) : QObject(), aborted(aborted) {}
public slots:
void doWork() {
while(!aborted.load() && !work_finished) /* do work */
}
};
class Controller : public QObject {
Q_OBJECT
QThread workerThread;
std::atomic<bool> aborted;
public:
Controller() : QObject() {
aborted.store(false);
Worker *worker = new Worker(aborted);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
connect(this, &Controller::aborted, worker, &Worker::abort);
}
void abort() { aborted.store(true); }
signals:
void startWork();
};
Controller *cont = new Controller;
emit cont->startWork(); // Start the loop
cont->abort(); // Stop the loop
Требуется логическое значение paused
.Controller
и Worker
требуется доступ для чтения / записи к нему.
Установите paused
в true
в Controller
при необходимости.
Во время цикла в Worker
, if(paused)
: QWaitCondition::wait()
до тех пор, пока из вызывающего потока не будет вызван QWaitCondition::wakeAll()
.
QMutex::lock
необходимо будет вызывать при каждом обращении к paused
.
class Worker : public QObject {
Q_OBJECT
bool &aborted, &paused;
QWaitCondition &waitCond;
QMutex &mutex;
public:
Worker(bool &aborted, bool &paused, QWaitCondition &waitCond, QQMutex &mutex)
: QObject(), aborted(aborted), paused(paused), waitCond(waitCond), mutex(mutex) {}
public slots:
void doWork() {
while(!aborted && !work_finished) {
//do work
mutex.lock();
if(paused) {
waitCond.wait(&mutex);
paused = false;
}
mutex.unlock();
}
}
void abort() { aborted = true; }
};
class Controller : public QObject {
Q_OBJECT
bool aborted=false, paused=false;
QWaitCondition waitCond;
QMutex mutex;
QThread workerThread;
public:
Controller() : QObject() {
Worker *worker = new Worker(aborted, paused, waitCond, mutex);
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
}
void abort() {
mutex.lock();
paused = true; // Worker starts waiting
mutex.unlock();
if(confirmed_by_user) aborted = true; // Don't need to lock because Worker is waiting
waitCond.wakeAll(); // Worker resumes loop
}
signals:
void startWork();
};
Controller *cont = new Controller();
emit cont->startWork(); // Start the loop
cont->abort(); // Stop the loop