暂停和恢复 QThread

发布于 2024-12-29 20:19:36 字数 256 浏览 3 评论 0原文

我最近开始学习 QThreads,并且我有一个程序在单独的线程中运行 4 小时长的循环(以便我可以继续使用 GUI)。我所追求的是,​​当用户单击暂停 qpushbutton 时,将暂停/挂起线程,并且当用户单击恢复 qpushbutton 时,程序应该恢复。我怎样才能实现这个目标?

我正在考虑从我的主类发送信号;但是,我不确定如何在线程中处理它们。是否可以在线程中处理从主类发送的信号?目前,我有线程向主类发出信号,并且工作正常,但我不确定如何从主类发送线程并在线程中接收它们。

I've recently began learning about QThreads and I've a program which runs a 4 hours long loop in a separate thread (so that I may continue to use the GUI). What I am after is, something that will pause/suspend the thread when the user clicks pause qpushbutton, and when the user clicks the resume qpushbutton, the program should resume. How may I achieve this?

I was thinking of sending signals from my main class; however, I'm not sure how I can handle them in the thread. Is it possible to handle signals sent from the main class in a thread? Currently, I have the thread emitting signals to the main class, and that works fine, but I'm not sure how to go about sending threads from the main class, and receiving them in the thread.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

绳情 2025-01-05 20:19:36

好的,所以我建议您创建内部线程变量,该变量将在循环的每个步骤中进行检查 + QWaitCondition 来恢复它。

  1. 创建暂停方法,您将在其中打开“暂停字段”(布尔?),不要忘记同步它
  2. 在您自己的循环中使用QWaitCondition(参见Qt文档)来暂停线程执行
  3. Create恢复方法,您将在其中关闭“暂停字段”并唤醒 QWaitCondition

    类 MyWorker:公共 QThread
    {
    私人的:
        QMutex同步;
        QWaitCondition 暂停条件;
        布尔暂停;
    
    民众:
        MyWorker(...): 暂停(假) {}
    
        无效简历()
        {
            同步.lock();
            暂停=假;
            同步.解锁();
            暂停Cond.wakeAll();
        }
    
        无效暂停()
        {
            同步.lock();
            暂停=真;
            同步.解锁();
        }
    
    受保护:
        无效运行()
        {
            while(someCondition) // 猜测这是你的循环
            {
                 同步.lock();
                 如果(暂停)
                     暂停Cond.wait(&sync); // 在这个地方,你的线程将停止执行,直到有人调用resume
                 同步.解锁();
    
                 // 执行你的操作
            }
        }
    };
    

Ok, so I suggest you make internal thread variable that will be checked in each step of your loop + QWaitCondition to resume it.

  1. Create pause method where you will switch on "pause field" (bool?), don't forget to synchronize it
  2. In your own loop use QWaitCondition (see Qt docs) to pause thread execution
  3. Create resume method where you will switch off "pause field" and wake 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
            }
        }
    };
    
给我一枪 2025-01-05 20:19:36

为了暂停工作线程,我使用了以下方法。

这是我的 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 是否未更改。如果它被改变了,我们返回并且什么都不做。要更改它,我们必须与工作线程竞争从 GUI 线程访问 CHECK。这就是为什么我们需要 while 块。我们将 while 块放在简历部分,尽管在大多数情况下,第一次尝试就会成功。但我们正在处理多线程,还记得吗?

To suspend a working thread I used the following approach.

Here is a part of my GUI.h file:

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

Here is a part of my GUI.cpp file:

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
    }
}

Here is my worker.h file:

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;
};

Here is a part of my worker.cpp file:

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();
}

The basic idea is to use QAtomicInt special features. In the worker thread we check if CHECK is unchanged. If it was changed we return and do nothing. To change it we have to compete with the worker thread for access to CHECK from the GUI thread. That is why we need while block. We put while block in the resume section, though in most cases it will succeed with the first attempt. But we are dealing with multi-threading, remember?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文