Приостановить и возобновить QThread - PullRequest
5 голосов
/ 31 января 2012

Я недавно начал изучать QThreads, и у меня есть программа, которая запускает цикл продолжительностью 4 часа в отдельном потоке (так что я могу продолжать использовать GUI). Что мне нужно, так это то, что будет приостанавливать / приостанавливать поток, когда пользователь нажимает кнопку pause qpushbutton, а когда пользователь нажимает кнопку возобновления qpushbutton, программа должна возобновить работу. Как мне этого добиться?

Я думал об отправке сигналов от моего основного класса; Однако я не уверен, как я могу справиться с ними в потоке. Можно ли обрабатывать сигналы, отправленные из основного класса в потоке? В настоящее время у меня есть поток, посылающий сигналы в основной класс, и он работает нормально, но я не уверен, как поступить с передачей потоков из основного класса и получением их в потоке.

Ответы [ 2 ]

14 голосов
/ 31 января 2012

Хорошо, поэтому я предлагаю вам сделать переменную внутреннего потока, которая будет проверяться на каждом шаге вашего цикла + QWaitCondition, чтобы возобновить ее.

  1. Создайте метод паузы, где вы включите «поле паузы» (bool?), Не забудьте синхронизировать его
  2. В вашем собственном цикле используйте QWaitCondition (смотрите документацию Qt), чтобы приостановить выполнение потока
  3. Создайте метод возобновления, в котором вы выключите «поле паузы» и разбудите QWaitCondition

    class MyWorker: public QThread
    {
    private:
        QMutex sync;
        QWaitCondition pauseCond;
        bool pause;
    
    public:
        MyWorker(...): pause(false) {}
    
        void resume()
        {
            sync.lock();
            pause = false;
            sync.unlock();
            pauseCond.wakeAll();
        }
    
        void pause()
        {
            sync.lock();
            pause = true;
            sync.unlock();
        }
    
    protected:
        void run()
        {
            while(someCondition) // gues it's your loop
            {
                 sync.lock();
                 if(pause)
                     pauseCond.wait(&sync); // in this place, your thread will stop to execute until someone calls resume
                 sync.unlock();
    
                 // do your operation
            }
        }
    };
    
1 голос
/ 08 апреля 2013

Чтобы приостановить рабочий поток, я использовал следующий подход.

Вот часть моего файла 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 в раздел резюме, хотя в большинстве случаев это будет успешным с первой попытки. Но мы имеем дело с многопоточностью, помните?

...