当前我有两个看起来像这样的类:
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()
相当昂贵,至少在很长的循环中使用(实际上多达数千个)。
所以我的问题是,如何才能以更好/更便宜的方式获得相同的结果?
答案 0 :(得分:1)
我目前知道三种替代解决方案。
QThread::requestInterruption
(由@Felix建议)根据QThread::isInterruptionRequested
:
请注意不要经常调用它,以保持较低的开销。
QCoreApplication::processEvents
并没有对性能或内存使用情况做任何评论,因此我认为QThread::requestInterruption
在这种情况下并不比QCoreApplication::processEvents
有所改进。
std::atomic
(由@Felix建议)原子对象的主要特征是从不同线程访问此包含的值不会导致数据争用[...]
布尔值可以存储在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
QWaitCondition
和QMutex
将需要布尔值Controller
。 Worker
和paused
需要对其具有读/写访问权限。
在需要时将true
中的Controller
设置为Worker
。
在if(paused)
中的循环期间,paused
:QWaitCondition::wait()
直到从调用线程中调用QWaitCondition::wakeAll()
。
每当访问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
时,都需要调用QMutex::lock
。
{{1}}