簡體   English   中英

本地QEventLoop-等待線程發出的信號-阻止處理來自主事件循環的事件

[英]Local QEventLoop - waiting for a signal from thread - prevent processing events from main event loop

我在“網絡”線程中等待來自“ 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_thread init_finished信號, loop.exec()應該阻止“網絡”線程執行。 但是當程序執行loop.exec()立即切換到執行do_something2 ,並且在do_something2完成后,程序的執行將在loop.exec()行之后loop.exec()do_something1

看起來本地事件循環處理了我期望的更多事件。

我想等待&io_thread::init_finished信號,而不處理本地事件循環中的其他事件。 我怎樣才能做到這一點?

事件循環處理所有可用的事件:您的期望不正確。 您想“暫停”某個事件的事件如何循環發生? 不行

您真正想要的-但您是在后面寫的-是要在I / O管理器完成初始化后啟動網絡管理線程。 這樣一來,什么都不必等待!

當您編寫偽同步代碼時,等待的事情就會發生,這就是所有麻煩的根源。 不要手動旋轉事件循環。 不要繼承QThread (除了使其成為RAII之外)。

最后,如果io_managernetwork_manager都不需要特殊的代碼來處理它們都在自己的線程中的事實,那就太好了。

首先,我們需要從此答案中包括函子分發代碼。

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

注意如何自動處理所有對象的生存期。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM