目前,我有两个看起来像这样的类:

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) pausedControllerWorker需要对其具有读/写访问权限。

需要时将paused设置为true中的Controller
Workerif(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

08-06 01:10
查看更多