简体   繁体   English

如何阻止运行阻塞永远循环的QThread?

[英]How to Stop a QThread That Runs a Blocking Forever Loop?

I have some rough code that I've been experimenting with: 我有一些粗略的代码,我一直在尝试:

someserver.cpp (a GUI) someserver.cpp(一个GUI)

#include "server.h"
#include "ui_server.h"

Server::Server(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::Server)
{
    ui->setupUi(this);
}

Server::~Server()
{
    delete ui;
}

void Server::onBtnStartClicked()
{
    QThread worker; 
    worker.start(); // Start worker thread that goes into an infinite loop with a blocking call
}

void Server::onBtnExitClicked()
{
    // How do I cleanly stop worker from running?
    QApplication::quit();
}

worker.cpp worker.cpp

#include "worker.h"

Worker::Worker(QObject *parent) :
    QThread(parent)
{
}

void Worker::run()
{
    for (;;)
    {
        // a blocking IO call here like pipe, or msgrcv
        // process data received
    }
}

Since the worker thread runs in a forever loop with a blocking IO call, how will I be able to structure this so that when the Stop button is pressed in the GUI thread, the worker thread is stopped cleanly? 由于工作线程在一个带有阻塞IO调用的永久循环中运行,我将如何构造它,以便在GUI线程中按下停止按钮时,工作线程会干净地停止?

You could of course put a boolean value within the for loop in Worker::run() checked every iteration, which breaks on ==true and is set by the gui Stop button. 你当然可以在每次迭代检查的Worker::run()的for循环中放置一个布尔值,它在== true时断开并由gui Stop按钮设置。 Of course, this won't quit the thread while execution is blocked. 当然,在执行被阻止时,这不会退出线程。

Probably better is to get rid of the for loop and use Qt's signals and slots to setup a callback function, connected to a signal like QIODevice::readyRead() . 可能更好的是摆脱for循环并使用Qt的信号和插槽来设置回调函数,连接到像QIODevice::readyRead()这样的信号。 These will be called only when there is information available in the socket/pipe whatever. 只有在套接字/管道中有可用信息时才会调用它们。 Any other time you'll be able to quit the thread with QThread::exit() . 任何其他时间你都可以使用QThread::exit()退出该线程。 You'll need to call QThread::exec() at some point as well to get the event loop going. 你需要在某个时刻调用QThread::exec()以获得事件循环。

in infinity loop paste this code and enjoy... 在无限循环中粘贴此代码并享受...

QTime dieTime = QTime::currentTime().addMSecs(1);
while( QTime::currentTime() < dieTime ) {
    QCoreApplication::processEvents( QEventLoop::AllEvents, 1);
}

don't forget 别忘了

#include <QCoreApplication>

#include <QTime>

see you 再见

First of all, you should set the parent for Worker to make it a child of another QObject , to clean it right way when the app is done, and create it on heap. 首先 ,您应该为Worker设置父级以使其成为另一个QObject的子级,以便在应用程序完成时以正确方式清除它,并在堆上创建它。 ie dynamic way . 即动态方式。

Second , simplest way to do what you want is to define slot which will set boolean member of Worker object and check it in each cycle to break endless loop. 其次 ,最简单的方法就是定义槽,它将设置Worker对象的布尔成员,并在每个周期中检查它以打破无限循环。 Don't forget to connect this slot to a right signal. 不要忘记将此插槽连接到正确的信号。

so you can put in constructor of the Server along the lines 所以你可以沿着行放置Server构造函数

QThread * worker = new QThread (this);
connect (this, SIGNAL(terminator()), worker, SLOT(terminate()));

in Server class declaration: Server类声明中:

signals: 
  terminator();

in class Worker : 在班级Worker

private: 
    bool terminate_; // don't forget initialize it to false
public slots: 
    void terminate(){terminate_ = true;}

and in the cycle: 并在周期中:

if (terminate_) {/* .. do exit things...*/; return; 

after that emit signal terminator() from server whenever you need. 之后,只要您需要,就从服务器发出信号terminator()

If you will need more complicated management then simple stop, you will probably need to protect your state variable with a mutex. 如果您需要更复杂的管理,那么简单的停止,您可能需要使用互斥锁保护您的状态变量。

To stop the worker thread it is necessary to add "stop requested" boolean flag, which should be tested at the beginning of each iteration. 要停止工作线程,必须添加“stop requested”布尔标志,该标志应在每次迭代开始时进行测试。 Also the synchronization is needed to read/write value of this boolean flag. 此外,还需要同步来读取/写入此布尔标志的值。

Here is the draft version: 这是草稿版本:

class Worker : public QThread {
    public:
        Worker() : stopRequested(false) {}

        void requestStop()
        {
            QMutexLocker locker(&mutex);
            stopRequested = true;
        }

        void run()
        {
            forever
            {
                if (stopRequested())
                    break;

                // a blocking IO call here like pipe, or msgrcv
                // process data received
            }
        }

    private:
        bool stopRequested()
        {
            QMutexLocker locker(&mutex);
            return stopRequested;
        }

        QMutex mutex;
        bool stopRequested;
};

Here you have the explanation of you're problem, i think. 我想你在这里解释了你的问题。 When your application will end, you should do something like this : 当您的应用程序结束时,您应该执行以下操作:

MyClass::~MyClass()
{
    m_thread->requestStop();
    m_thread->wait(); // Force the thread trying to destroy 
                      // an object of type MyClass, to wait 
                      // the end of the execution of m_thread
    delete m_thread;
}

wait() is the solution for your synchronisation problem. wait()是同步问题的解决方案。 You could also change requestStop() to : 您还可以将requestStop()更改为:

void requestStop()
{
    mutex.lock();
    stopRequested = true;
    mutex.unlock();
    wait();
}

Removing QMutexLocker is important to avoid a Deadlock situation. 删除QMutexLocker对于避免死锁情况非常重要。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM