【问题标题】:Local QEventLoop - waiting for a signal from thread - prevent processing events from main event loop本地 QEventLoop - 等待来自线程的信号 - 防止处理来自主事件循环的事件
【发布时间】:2018-05-05 10:17:08
【问题描述】:

我在“网络”线程中等待来自“io”线程的信号时遇到问题。 在io_thread.cpp 我有:

void io_thread::run()
{
  app_log = new app_logger();
  room_log_mgr = new room_logger_manager(this);

  emit init_finished();
  exec();
  QCoreApplication::processEvents();

  delete room_log_mgr;
  delete app_log;
}

在“主”线程中,我有两个排队事件(do_something1do_something2 在“网络”线程中执行):

QMetaObject::invokeMethod(network_obj, "do_something1", Qt::QueuedConnection);
QMetaObject::invokeMethod(network_obj, "do_something2", Qt::QueuedConnection);

do_something1 方法中我有这个代码:

QEventLoop loop;
QObject::connect(&logger_thread, &io_thread::init_finished, &loop, &QEventLoop::quit, Qt::BlockingQueuedConnection);
logger_thread.start();
loop.exec();

所以我等待来自io_threadloop.exec()init_finished 信号应该阻止“网络”线程执行。但是当程序执行loop.exec()然后立即切换到执行do_something2,并在do_something2完成程序执行后回到do_something1loop.exec()之后。

看起来本地事件循环处理了我所期望的更多事件。

我想等待&io_thread::init_finished 信号而不处理本地事件循环中的其他事件。我该怎么做?

【问题讨论】:

    标签: c++ multithreading qt


    【解决方案1】:

    事件循环处理所有可用的事件:您的期望不正确。事件循环如何判断您想“暂停”某个事件?不能。

    你真正想要的——但你是倒着写的——是让网络管理线程在 I/O 管理器完成初始化后启动。这样就不需要等待任何东西了!

    在编写伪同步代码时会发生等待,这就是所有问题的根源。不要手动旋转事件循环。不要从QThread 继承(除了使其成为 RAII)。

    最后,如果io_managernetwork_manager 都不需要特殊代码来处理它们在自己的线程中的事实,那就太好了。

    首先,我们需要包含来自this answer 的函子调度代码。

    // https://github.com/KubaO/stackoverflown/tree/master/questions/thread-sync-50188307
    #include <QtCore>
    
    namespace detail { template <typename F> struct FEvent : QEvent {
       const QObject *const obj;
       const QMetaObject *const type = obj->metaObject();
       typename std::decay<F>::type fun;
       template <typename Fun>
       FEvent(const QObject *obj, Fun &&fun) :
          QEvent(QEvent::None), obj(obj), fun(std::forward<Fun>(fun)) {}
       ~FEvent() { // ensure that the object is not being destructed
          if (obj->metaObject()->inherits(type)) fun();
       }
    }; }
    
    template <typename F> static void post(QObject *obj, F &&fun) {
       Q_ASSERT(!qobject_cast<QThread*>(obj));
       QCoreApplication::postEvent(obj, new detail::FEvent<F>(obj, std::forward<F>(fun)));
    }
    

    然后,我们需要一个真正的 RAII 线程,它总是可以安全地破坏:

    class Thread final : public QThread {
       Q_OBJECT
       void run() override {
          if (!eventDispatcher())
             QThread::run();
       }
    public:
       using QThread::QThread;
       using QThread::exec;
       ~Thread() override {
          requestInterruption();
          quit();
          wait();
       }
       template <typename F> void on_start(F &&fun) {
          connect(this, &QThread::started, std::forward<F>(fun));
       }
    };
    

    started 信号在线程内发出,任何传递给on_start 的函子都有效地注入到线程中。 run() 方法被重新实现为仅在之前未运行的情况下启动事件循环。这样我们就可以注入整个线程体来构造对象并旋转事件循环。

    I/O 和网络管理器是简单的对象,而不是线程。我们将io_managernetwork_manager 的构造推迟到它们各自的线程。这样,各个对象就不必知道它们在哪个线程上运行。

    #include <memory>
    
    using app_logger = QObject;
    using room_logger_manager = QObject;
    
    class io_manager : public QObject {
       Q_OBJECT
       app_logger app_log{this};
       room_logger_manager room_log_mgr{this};
    public:
       using QObject::QObject;
    };
    
    class network_manager : public QObject {
       Q_OBJECT
    public:
       network_manager(QObject *parent = {}) : QObject(parent) {
          do_something1();
          do_something2();
       }
       void do_something1() { qDebug() << __FUNCTION__; }
       void do_something2() { qDebug() << __FUNCTION__; qApp->quit(); }
    };
    
    int main(int argc, char *argv[]) {
       QCoreApplication app{argc, argv};
       QPointer<io_manager> iomgr; //optional
       QPointer<network_manager> netmgr; //optional
       Thread io_thread, network_thread;
       io_thread.on_start([&]{
          qDebug() << "I/O thread is running.";
          io_manager mgr;
          iomgr = &mgr;
          network_thread.start();
          io_thread.exec();
       });
       network_thread.on_start([&]{
          qDebug() << "Network thread is running.";
          network_manager mgr;
          netmgr = &mgr;
          network_thread.exec();
       });
       io_thread.start();
       return app.exec(); // RAII all the way!
    }
    #include "main.moc"
    

    输出:

    I/O thread is running.
    Network thread is running.
    do_something1
    do_something2
    

    注意所有对象的生命周期是如何自动处理的。

    【讨论】:

      猜你喜欢
      • 2019-07-08
      • 2018-07-16
      • 1970-01-01
      • 1970-01-01
      • 2013-05-20
      • 1970-01-01
      • 2022-11-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多