首页
学习
活动
专区
圈层
工具
发布

Qt同步法
EN

Stack Overflow用户
提问于 2015-03-27 09:57:14
回答 2查看 2.1K关注 0票数 4

我是一个纯粹的Qt初学者,目前我想了解它的基本概念,以及如何正确地使用它们。我的问题可能看上去有些“破烂”,我想为此事先道歉。话虽如此,问题是:

我想要实现一个方法,它“阻塞”直到处理完成(出于什么原因.)。为了完成内部操作,我必须使用异步类,当信号在后台完成工作时发出信号。我想用一种阻塞的方式:

代码语言:javascript
复制
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启动之前,当它已经“完成”时,会发生什么呢?

一个学院告诉我,我可以用

代码语言:javascript
复制
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当时甚至没有运行.

我希望我的问题能够被专家理解。

非常感谢,迈克尔

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-03-28 14:31:47

关键是QObject::connect

代码语言:javascript
复制
QObject::connect(x, SIGNAL(finished()), &myLoop, SLOT(quit()));

该函数表示,当发出finished()时,应该在事件发生后调用myLoop.quit()。所以在你的例子中,

代码语言:javascript
复制
<--------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::连接,最后一个论点是

代码语言:javascript
复制
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函数时调用一个函数。

票数 2
EN

Stack Overflow用户

发布于 2015-03-28 14:29:22

关于你问题的第一部分:是的,这是可行的。

实际上是在Qxt中它的一个类

为了理解它是如何工作的,您需要了解事件处理在一般情况下是如何工作的,而这个特定的黑客虽然是一个总体上不好的想法,但它是一个很好的学习方法。

让我们用一个主循环对一个简化的调用堆栈进行成像:

代码语言:javascript
复制
6 myslot()
5 magic
4 someEvent.emit
3 eventloop.exec
2 qapplication.exec
1 main

只要您不从my时隙()返回,“魔术”就不能调用连接到同一信号的任何其他插槽,也不能处理任何其他信号。

现在,使用嵌套的事件循环“信号服务生”攻击:

代码语言:javascript
复制
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时,信号发射不再是堆叠调用。看起来更像这样:

代码语言:javascript
复制
5 eventloop.putQueue
4 someSignal.emit
3 eventloop.exec
2 qapplication.exec
1 main

然后发出返回

代码语言:javascript
复制
3 eventloop.exec
2 qapplication.exec
1 main

事件循环将你的插槽放在堆栈上。

代码语言:javascript
复制
5 myslot()
4 eventloop.pop
3 eventloop.exec
2 qapplication.exec
1 main

因此,在调用时隙时,您的信号已经从堆栈中删除,所有局部变量都消失了。这与DirectConnection有很大的不同,对于像QObject::deleteLater()这样的其他东西,理解也很重要。

要回答哪个事件循环执行您的队列的问题:堆栈顶部的那个。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29297854

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档