c++ - 使用 "infinite"循环正确处理 QtConcurrent

标签 c++ multithreading qt qtconcurrent

我正在编写一个程序,用户可以在其中查看摄像机的视频并进行录制。我正在使用 QtMEL 库来获取相机输入和记录。当相机启动时,它会调用 QTconcurrent::run() 函数来抓取帧。该函数内部有一个无限循环,用于检查相机输入是否已关闭,如果是,则退出循环。像这样的事情:

Q_FOREVER {
    frame = captureFrame();

    //Do stuff

    //check if we must finish grabbing
    if (isStopRequest() || isPauseRequest())
        break;
}

如果用户关闭相机(这将停止帧抓取并结束线程),然后存在程序,一切都很好。问题是当用户退出程序时相机仍然处于打开状态。如果发生这种情况,进程将永远运行或抛出段错误。

我已经搜索了文档并根据它:

Note that the QFuture returned by QtConcurrent::run() does not support canceling, pausing, or progress reporting. The QFuture returned can only be used to query for the running/finished status and the return value of the function.

我想出了一个解决这个问题的方法,但我不知道它是否是最好的,我想从比我更有经验的人那里获得更多的见解。

基本上我像这样重新实现了 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
}

关于c++ - 使用 "infinite"循环正确处理 QtConcurrent,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25171093/

相关文章:

multithreading - Threads.@spawn 和 Threads.@threads 有什么区别?

python - 从子线程停止主线程

c++ - QTimer 启动函数到底发生了什么?

c++ - 如何使用 KCachegrind 和 Callgrind 来测量我的部分代码?

c++ - 多级指针,栈还是堆?

c++ - CMake在编译使用OpenCV的项目时遇到问题

C++ Qt 命名空间和循环依赖

c++ - 如何使用朴素贝叶斯算法对未知记录进行分类

python - 在 Python 线程中使用 Intvar/DoubleVar 是否安全?

c++ - 无法从客户端向服务器发送消息