如何停止运行阻塞 Forever 循环的 QThread?

Posted

技术标签:

【中文标题】如何停止运行阻塞 Forever 循环的 QThread?【英文标题】:How to Stop a QThread That Runs a Blocking Forever Loop? 【发布时间】:2012-02-11 20:35:11 【问题描述】:

我有一些我一直在试验的粗略代码:

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

#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
    

由于工作线程在阻塞 IO 调用的永久循环中运行,我将如何构造它以便在 GUI 线程中按下停止按钮时,工作线程完全停止?

【问题讨论】:

【参考方案1】:

您当然可以在 Worker::run() 中的 for 循环中放置一个布尔值,检查每次迭代,它在 ==true 时中断并由 gui 停止按钮设置。当然,这不会在执行被阻塞时退出线程。

可能更好的是摆脱for 循环并使用Qt 的信号和槽来设置回调函数,连接到像QIODevice::readyRead() 这样的信号。仅当套接字/管道中有可用信息时才会调用它们。任何其他时间您都可以使用QThread::exit() 退出该线程。您还需要在某个时候调用 QThread::exec() 以启动事件循环。

【讨论】:

问题是阻塞调用是一个 System V 消息队列,所以我认为我不能使用 QIODevice 之类的东西来为此设置回调函数。 @CodingDistrict 我不熟悉这些,但是当消息准备好时肯定有某种回调装置吗?不管怎样,我认为你可以继承 QLocalSocket 并为它制作一个包装器。【参考方案2】:

在无限循环中粘贴此代码并享受...

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

别忘了

#include <QCoreApplication>

#include <QTime>

再见

【讨论】:

【参考方案3】:

要停止工作线程,必须添加“停止请求”布尔标志,应在每次迭代开始时对其进行测试。还需要同步来读取/写入此布尔标志的值。

这是草稿版本:

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

【讨论】:

这里同步的意义何在?过度同步并不比同步不足好。同样正如@Matt Phillips 提到的,如果线程在 IO 操作中被阻塞,这也无济于事。 还需要将信号(类似于“停止”)添加到Worker 类并在永远循环后立即发出。 UI 级类应与信号连接并接收“停止”通知。【参考方案4】:

首先,您应该将Worker 的父级设置为另一个QObject 的子级,以便在应用程序完成后正确清理它,并在堆上创建它.即动态方式。

第二,做你想做的最简单的方法是定义槽,它将设置Worker对象的布尔成员并在每个循环中检查它以打破无限循环。不要忘记将此插槽连接到正确的信号。

所以你可以沿线放入Server的构造函数

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

Server类声明中:

signals: 
  terminator();

在课堂上Worker

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

在循环中:

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

之后,在需要时从服务器发出信号terminator()

如果您需要更复杂的管理而不是简单的停止,您可能需要使用互斥锁来保护您的状态变量。

【讨论】:

【参考方案5】:

Here 你有问题的解释,我想。当您的应用程序结束时,您应该执行以下操作:

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() 是您同步问题的解决方案。您也可以将requestStop() 更改为:

void requestStop()

    mutex.lock();
    stopRequested = true;
    mutex.unlock();
    wait();

删除QMutexLocker 对于避免死锁情况很重要。

【讨论】:

以上是关于如何停止运行阻塞 Forever 循环的 QThread?的主要内容,如果未能解决你的问题,请参考以下文章

如何停止 Python Websocket 客户端“ws.run_forever”

时间循环 asyncio 协程

如何让nodejs在linux后台运行

Qt(PyQt)事件循环中的xmlrpc?

forever让node.js持久运行

蟒蛇3; Websockets 服务器和 HTTP 服务器 - run_forever 和 serve_forever