为什么应该避免嵌套 QEventLoops? [英] Why should nesting of QEventLoops be avoided?

查看:18
本文介绍了为什么应该避免嵌套 QEventLoops?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在他的 Qt 事件循环、网络和 I/O API 演讲中, Thiago Macieira 提到应该避免 QEventLoop 的嵌套:

In his Qt event loop, networking and I/O API talk, Thiago Macieira mentions that nesting of QEventLoop's should be avoided:

QEventLoop 用于嵌套事件循环......如果可以,请避免使用它,因为它会产生许多问题:事情可能会重新进入,套接字或计时器的新激活是您意想不到的.

QEventLoop is for nesting event Loops... Avoid it if you can because it creates a number of problems: things might reenter, new activations of sockets or timers that you were not expecting.

任何人都可以扩展他所指的内容吗?我维护了很多使用模态对话框的代码,这些代码在调用 exec() 时在内部嵌套了一个新的事件循环,所以我很想知道这可能导致什么样的问题.

Can anybody expand on what he is referring to? I maintain a lot of code that uses modal dialogs which internally nest a new event loop when exec() is called so I'm very interested in knowing what kind of problems this may lead to.

推荐答案

  1. 嵌套的事件循环会消耗 1-2kb 的堆栈.在典型的 32kb L1 缓存 CPU 上,它占据了 L1 数据缓存的 5%,无所适从.

  1. A nested event loop costs you 1-2kb of stack. It takes up 5% of the L1 data cache on typical 32kb L1 cache CPUs, give-or-take.

它可以重新输入调用堆栈中已有的任何代码.不能保证任何代码都被设计为可重入的.我说的是你的代码,而不是 Qt 的代码.它可以重新输入已启动此事件循环的代码,除非您明确控制此递归,否则无法保证您最终不会耗尽堆栈空间.

It has the capacity to reenter any code already on the call stack. There are no guarantees that any of that code was designed to be reentrant. I'm talking about your code, not Qt's code. It can reenter code that has started this event loop, and unless you explicitly control this recursion, there are no guarantees that you won't eventually run out of stack space.

在当前的 Qt 中,由于长期存在的 API 错误或平台不足,您必须在两个地方使用嵌套的 exec:QDrag 和平台文件对话框(在某些平台上).您根本不需要在其他任何地方使用它.您不需要非平台模式对话框的嵌套事件循环.

In current Qt, there are two places where, due to a long standing API bugs or platform inadequacies, you have to use nested exec: QDrag and platform file dialogs (on some platforms). You simply don't need to use it anywhere else. You do not need a nested event loop for non-platform modal dialogs.

重新进入事件循环通常是由编写伪同步代码引起的,其中有人感叹缺少 yield()(co_yieldco_await 现在已经在 C++ 中了!),把头埋在沙子里,用 exec() 代替.这样的代码通常最终成为几乎不好吃的意大利面,并且是不必要的.

Reentering the event loop is usually caused by writing pseudo-synchronous code where one laments the supposed lack of yield() (co_yield and co_await has landed in C++ now!), hides one's head in the sand and uses exec() instead. Such code typically ends up being barely palatable spaghetti and is unnecessary.

对于现代 C++,使用 C++20 协程是值得的;有一些基于Qt的实验,易于构建.

For modern C++, using the C++20 coroutines is worthwhile; there are some Qt-based experiments around, easy to build on.

有堆栈协程的 Qt 原生实现:Skycoder42/QtCoroutings - 最近的一个项目,以及较旧的 ckamm/qt-coroutine.我不确定后面的代码有多新鲜.看起来这一切都在某个时候奏效了.

There are Qt-native implementations of stackful coroutines: Skycoder42/QtCoroutings - a recent project, and the older ckamm/qt-coroutine. I'm not sure how fresh the latter code is. It looks that it all worked at some point.

在没有协程的情况下干净地编写异步代码通常是通过状态机来完成的,例如参见这个答案,以及QP 框架,用于与 QStateMachine 不同的实现.

Writing asynchronous code cleanly without coroutines is usually accomplished through state machines, see this answer for an example, and QP framework for an implementation different from QStateMachine.

个人轶事:我迫不及待地希望 C++ 协程可以投入生产,现在我用 golang 编写异步通信代码,并将其静态链接到 Qt 应用程序中.效果很好,垃圾收集器不明显,并且代码比带有协程的 C++ 更容易阅读和编写.我有很多代码是用 C++ 协程 TS 写的,但都搬到了 golang 上,我不后悔.

Personal anecdote: I couldn't wait for C++ coroutines to become production-ready, and I now write asynchronous communication code in golang, and statically link that into a Qt application. Works great, the garbage collector is unnoticeable, and the code is way easier to read and write than C++ with coroutines. I had a lot of code written using C++ coroutines TS, but moved it all to golang and I don't regret it.

这篇关于为什么应该避免嵌套 QEventLoops?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆