[英]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_something1
和do_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_manager
和network_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_manager
和network_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.