正确处理 QtT并发"infinite"循环
Correctly handling QtConcurrent with "infinite" loop
我正在做一个程序,在这个程序中,用户可以看到来自摄像机的视频并记录下来。我正在使用QtMEL库来获取相机馈送和记录。当相机启动时,它调用一个带有QTconcurrent::run()的函数来抓取帧。在这个函数内部有一个无限循环,检查相机馈送是否关闭,如果是,则退出循环。像这样:
Q_FOREVER {
frame = captureFrame();
//Do stuff
//check if we must finish grabbing
if (isStopRequest() || isPauseRequest())
break;
}
如果用户关闭相机(这会停止抓取帧并结束线程),然后存在程序,一切都很好。问题是当用户退出程序时,相机馈送仍然打开。如果发生这种情况,进程将永远运行或抛出分段错误。
我已经搜索了文档,并根据它:
注意QtConcurrent::run()返回的QFuture不支持取消、暂停或进度报告。返回的QFuture只能用于查询函数的运行/完成状态和返回值。
我想到了一个解决这个问题的方法,我不知道它是否是最好的,我希望从比我更有经验的人那里得到更多的见解。
基本上我像这样重新实现了closeEvent:
void MainWindow::closeEvent(QCloseEvent *event) {
if(camera1->state() == AbstractGrabber::StoppedState)
event->accept();
else {
camera1->stop();
connect(camera1, SIGNAL(stateChanged(AbstractGrabber::State)),
SLOT(exitWindow(AbstractGrabber::State)));
event->ignore();
}
}
在槽上:
void MainWindow::exitWindow(AbstractGrabber::State grabberState) {
if(grabberState == AbstractGrabber::StoppedState) this->close();
}
也许我太天真了,但在我看来,有一个更好的解决方案。有经验的人能帮我吗?
提前感谢您的时间。
假设,停止相机线程将很快,并且您不应该忽略事件。只需等待,直到相机线程完成,然后从closeEvent
返回。closeEvent
甚至不需要参与。您并不真正关心窗口是否关闭,您关心的是应用程序事件循环是否已退出。
很容易确定相机线程何时结束,而无需与帧捕获器类交互。QtConcurrent::run
在从全局线程池中取出的线程上执行函子,也就是QThreadPool::globalInstance()
。activeThreadCount
方法返回正在忙于完成提交给它们的工作的线程数。
你可以这样实现它:
class CameraStopper() {
Q_DISABLE_COPY(CameraStopper)
AbstractGrabber m_camera;
public:
explicit CameraStopper(AbstractGrabber * cam) : m_camera(cam) {}
~CameraStopper() {
m_camera->stop();
while (QThreadPool::globalInstance()->activeThreadCount())
QThread::yieldCurrentThread();
}
}
AbstractGrabber * MainWindow::camera() const { return camera1; }
int main(int argc, char ** argv) {
QApplication app(argc, argv);
MainWindow w;
CameraStopper stopper(w.camera());
w.show();
return app.exec();
}
当然,这是假设全局池中的其他线程没有被卡住而没有发出停止信号。CameraStopper
需要向所有这样的异步任务发出停止信号。还要确保如果您通过reserveThread
保留了任何线程,它们在您等待相机停止之前被正确释放。
你可以做的另一件事是使用一些每线程标志来停止线程。在Qt 5.2之前,这个标志只有在线程中运行QEventLoop
时才可用——在该线程的实例上调用QThread::quit()
将结束事件循环。自Qt 5.2以来,有另一个独立于事件循环的标志。该标志通过QThread::requestInterruption
设置,并通过QThread::isInterruptionRequested
检查。
您需要保持线程的全局列表,以便您可以轻松地退出它们,因为QThreadPool
不公开其线程。可以这样做:
class ThreadRegistry {
Q_DISABLE_COPY(ThreadRegistry)
friend class ThreadRegistration;
QMutex m_mutex;
QList<QThread*> m_threads;
static QScopedPointer<ThreadRegistry> m_instance;
static ThreadRegistry * instance() {
if (!m_instance) m_instance.reset(new ThreadRegistry);
return m_instance;
}
public:
ThreadRegistry() {}
~ThreadRegistry() { quitThreads(); waitThreads(); }
void quitThreads() {
QMutexLocker lock(&m_mutex);
QList<QThread*> & threads(m_threads);
for (auto thread : threads) {
thread->quit(); // Quit the thread's event loop, if any
#if QT_VERSION>=QT_VERSION_CHECK(5,2,0)
thread->requestCancellation();
#endif
}
}
void waitThreads() {
forever {
QMutexLocker lock(&m_mutex);
int threadCount = m_threads.count();
lock.unlock();
if (!threadCount) break;
QThread::yieldCurrentThread();
}
}
};
class ThreadRegistration {
Q_DISABLE_COPY(ThreadRegistration)
public:
ThreadRegistration() {
QMutexLocker lock(&ThreadRegistry::instance()->m_mutex);
ThreadRegistry::instance()->m_threads << QThread::currentThread();
}
~ThreadRegistration() {
QMutexLocker lock(&ThreadRegistry::instance()->m_mutex);
ThreadRegistry::instance()->m_threads.removeAll(QThread::currentThread());
}
};
最后,Qt 5.2及以上版本的可运行文件如下:
QtConcurrent::run([this]{
ThreadRegistration reg;
while (!QThread::currentThread()->isInterruptionRequested()) {
frame = captureFrame();
//Do stuff
}
}
对于Qt 5.0和5.1,您可以利用零超时计时器来连续运行一些代码,只要事件循环没有退出:
QtConcurrent::run([this]{
ThreadRegistration reg;
QTimer timer;
timer.setInterval(0);
timer.setSingleShot(false);
timer.start();
QObject::connect(&timer, &QTimer::timeout, [this]{
frame = captureFrame();
//Do stuff
}
QEventLoop loop;
loop.run();
}
和main
看起来如下:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
ThreadRegistry registry;
MainWindow w;
w.show();
return app.exec();
// ThreadRegistry's destructor will stop the threads
// and wait for them to finish
}
可以在类的析构函数中等待线程完成。这可以使用QFuture::waitForFinished ()
来完成。您可以将线程的未来作为类成员:
QFuture<void> future;
并在新线程中运行函数:
future = QtConcurrent::run(this,&MyClass::myFunction);
在发送停止请求后的析构函数中,您可以等待线程完成:
camera1->stop();
if(future.isRunning())
future.waitForFinished();
您的循环已经检查是否已关闭或暂停相机馈送。为什么不添加另一个检查来查看用户是否关闭了程序呢?
顺便说一下,QtConcurrent::run()
和QRunnable
是为短期任务设计的。对于无限循环(永久线程),官方文档建议使用QThread
: http://qt-project.org/doc/qt-5/threads-technologies.html
下面是QThread中循环的重新实现:
// CameraThread subclasses QThread
CameraThread::run()
{
while (!isStopRequest()
&& !isPauseRequest()
&& !this->isInterruptionRequested())
{
frame = captureFrame();
//Do stuff
}
}
当你退出时,调用QThread::requestInterruption()
使isInterruptionRequested()
返回true并终止循环
- 如何循环打印顶点结构
- 如何在C++中从两个单独的for循环中添加两个数组
- C++我的数学有什么问题,为什么我的代码不能正确循环
- 正在尝试了解输入验证循环
- std::map<struct,struct>::find 找不到匹配项,但是如果我循环通过 begin() 到 end(),我在那里看到匹配项
- 循环后如何继续阅读
- Ardunio UNO解决了多个重叠的定时器循环
- Eigen如何在容器循环中干净地附加矩阵
- 在某些循环内使用vector.push_back时出现分段错误
- 我正在使用嵌套的while循环来解析具有多行的文本文件,但由于某种原因,它只通过第一行,我不知道为什么
- 为什么我的for循环不能正确获取argv
- 如何声明特征矩阵,然后通过嵌套循环初始化它
- while循环中while循环的时间复杂度是多少
- C++中的高效循环缓冲区,它将被传递给C样式数组函数参数
- 为什么在这个代码结束循环中没有得到结束
- 在基于范围的for循环中使用结构化绑定声明
- 用于C++中带有数组和指针的循环
- 循环中的随机函数
- 文件I/O Infinite循环C EOF
- 正确处理 QtT并发"infinite"循环