如何停止运行阻塞 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?的主要内容,如果未能解决你的问题,请参考以下文章