目前,我有两个看起来像这样的类:
class Worker : public QObject
{
Q_OBJECT
bool aborted = false;
public:
Worker() : QObject() {}
public slots:
void abort() { aborted = true; }
void doWork()
{
while(!aborted && !work_finished)
{
//do work
QCoreApplication::processEvents();
}
}
};
class Controller : public QObject
{
Q_OBJECT
QThread workerThread;
public:
Controller() : QObject()
{
Worker *worker = new Worker;
worker->moveToThread(&workerThread);
connect(&workerThread, &QThread::finished, worker, &Worker::deleteLater);
connect(this, &Controller::startWork, worker, &Worker::doWork);
connect(this, &Controller::aborted, worker, &Worker::abort);
}
signals:
void startWork();
void aborted();
};
Controller *cont = new Controller;
emit cont->startWork(); // Start the loop
emit cont->aborted(); // Stop the loop
因此,想法是在
Worker
线程中运行一个循环,可以从Controller
线程中停止该循环。在示例中,这是通过调用
QCoreApplication::processEvents()
来完成的,它允许信号在将控制权返回到循环之前调用插槽。重要的是,循环仅在迭代的开始或结束时停止。
尽管这很好用,但我认为
QCoreApplication::processEvents()
相当昂贵,至少在很长的循环中使用时(实际上多达数千个)。所以我的问题是,如何才能以更好/更便宜的方式获得相同的结果?
最佳答案
目前,我知道三种替代解决方案。
1. QThread::requestInterruption
(@Felix建议)
根据 QThread::isInterruptionRequested
:
尽管 QCoreApplication::processEvents
对性能或内存使用情况没有任何评论,所以在这种情况下,我不认为QThread::requestInterruption
是对QCoreApplication::processEvents
的改进。
2. std::atomic
(@Felix建议)
bool(boolean) 值可以存储在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
3.
QWaitCondition
和 QMutex
需要一个 bool(boolean)
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