我是一个纯粹的Qt初学者,目前我想了解它的基本概念,以及如何正确地使用它们。我的问题可能看上去有些“破烂”,我想为此事先道歉。话虽如此,问题是:
我想要实现一个方法,它“阻塞”直到处理完成(出于什么原因.)。为了完成内部操作,我必须使用异步类,当信号在后台完成工作时发出信号。我想用一种阻塞的方式:
QEventLoop myLoop;
workerClass x;
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));
/* x is asnchrounous and will emit "finished" when ist job is done */
x.doTheJob();
myLoop.exec();那能行吗?根据其他一些帖子-应该是的。但到底是怎么回事?
当刚刚创建一个新的QEventLoop实例时,它是否自动意味着,从myLoop发出的信号在这个新的处理程序中被自动处理?此外,在启动作业之后,循环会在一些时间内启动。在myLopp启动之前,当它已经“完成”时,会发生什么呢?
一个学院告诉我,我可以用
QEventLoop myLoop;
workerClass x;
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));
/* doTheJob is a slot now */
QMetaObject::invokeMethod(x, "doTheJob", Qt::QueuedConnection);
myLoop.exec();我理解,在本例中,doTheJob是从事件循环调用的。但是从哪个事件循环?invokeMethod()没有被告知在特定的事件循环( myLoop )中调用方法,那么为什么要将事件提交给myLoop,而将其发送到“外部”循环中呢?事实上,myLoop当时甚至没有运行.
我希望我的问题能够被专家理解。
非常感谢,迈克尔
发布于 2015-03-28 14:31:47
关键是QObject::connect
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));该函数表示,当发出finished()时,应该在事件发生后调用myLoop.quit()。所以在你的例子中,
<--------thread A--------> | <--------thread B-------->
QEventLoop myLoop;
workerClass x;
x.doTheJob(); starts thread B
sleep in exec() workerClass::JobA();
sleeping..... workerClass::JobB();
sleeping..... workerClass::JobC();
sleeping..... workerClass::JobD();
sleeping..... workerClass::finished();
sleeping.....
wake up by quit();必须在线程A休眠后调用myLoop.quit,否则线程A可能会永远休眠,因为quit可能在休眠前被调用。你必须找到一种方法来指定这一点。但是怎么做呢?看看QObject::连接,最后一个论点是
Qt::ConnectionType type = Qt::AutoConnection关于Qt::自动连接,
(默认)如果接收方驻留在发出信号的线程中,则使用Qt::DirectConnection。否则,将使用Qt::QueuedConnection。
信号在线程B中发出,接收器myLoop位于线程A中,这意味着您使用的是Qt::QueuedConnection:
当控件返回到接收方线程的事件循环时,将调用插槽。插槽在接收器的线程中执行。
当控件返回事件循环myLoop.exec时,将调用插槽myLoop.exec。换句话说,只有在运行quit时才调用exec。这正是我们想要的,一切都很顺利。因此,您的第一个示例总是正确运行,因为信号和插槽正在使用Qt::QueuedConnection (从默认值Qt::AutoConnection)连接在一起。第二个示例运行良好,因为QObject::connect也是相同的。
如果你把它改为Qt::DirectConnection,故事就不一样了。quit在线程B中被调用,在exec和线程A将永远休眠之前调用quit是可能的。因此,在这个场景中,您不应该使用Qt::DirectConnection。
这个故事是关于QObject::connect和线程的。QMetaObject::invokeMethod与此无关,它只是在线程执行exec函数时调用一个函数。
发布于 2015-03-28 14:29:22
关于你问题的第一部分:是的,这是可行的。
实际上是在Qxt中它的一个类。
为了理解它是如何工作的,您需要了解事件处理在一般情况下是如何工作的,而这个特定的黑客虽然是一个总体上不好的想法,但它是一个很好的学习方法。
让我们用一个主循环对一个简化的调用堆栈进行成像:
6 myslot()
5 magic
4 someEvent.emit
3 eventloop.exec
2 qapplication.exec
1 main只要您不从my时隙()返回,“魔术”就不能调用连接到同一信号的任何其他插槽,也不能处理任何其他信号。
现在,使用嵌套的事件循环“信号服务生”攻击:
10 myslot2()
9 magic
8 someEvent2.emit
7 eventloop.exec
6 myslot()
5 magic
4 someEvent.emit
3 eventloop.exec
2 qapplication.exec
1 main我们仍然不能继续处理阻塞的事件,但是我们可以处理新的事件,比如连接到退出的信号。
这不是重新进入,通常是一个非常糟糕的想法。您很可能最终会很难调试代码,因为您可能正在从您不期望的堆栈中调用插槽。
避免火灾前执行问题的方法是将信号连接到设置标志的插槽上。如果在exec之前设置了标志,则为只是别执教。
不过,为了解释一下,让我们看看您的方法是如何工作的(它确实工作):
当您使用Qt::QueuedConnection时,信号发射不再是堆叠调用。看起来更像这样:
5 eventloop.putQueue
4 someSignal.emit
3 eventloop.exec
2 qapplication.exec
1 main然后发出返回
3 eventloop.exec
2 qapplication.exec
1 main事件循环将你的插槽放在堆栈上。
5 myslot()
4 eventloop.pop
3 eventloop.exec
2 qapplication.exec
1 main因此,在调用时隙时,您的信号已经从堆栈中删除,所有局部变量都消失了。这与DirectConnection有很大的不同,对于像QObject::deleteLater()这样的其他东西,理解也很重要。
要回答哪个事件循环执行您的队列的问题:堆栈顶部的那个。
https://stackoverflow.com/questions/29297854
复制相似问题