在调用堆栈上很好的 C/C++ 中的事件循环实现

Posted

技术标签:

【中文标题】在调用堆栈上很好的 C/C++ 中的事件循环实现【英文标题】:Implementations for event loop in C/C++ that's nice on the call stack 【发布时间】:2015-10-24 23:04:18 【问题描述】:

TL;DR

在 C/C++ 中可以很好地管理调用堆栈的事件循环的最佳实现是什么?

为什么? 基准/比较? 文学/出版物/文档?

事件循环,问题

它们是一个简单的概念:等待事件、处理事件、等待更多事件。

我在回顾一些旧项目时发现了一个简单(而且有点糟糕)的搜索引擎实现,我对正确的事件循环方式引发了我的好奇心。 p>

当时我做了这样(非常)简化的例子:

int wait_for_query();
int handle_query();

int main(int argc, const char** argv) 
  return wait_for_query();


int wait_for_query() 
  // Do some stuff
  return handle_query();


int handle_query() 
  // Handle it
  // if query is quit, return quit();
  return wait_for_query();


int quit() 
  return 0;

此实现依赖于调用链来实现“事件循环”。我使用引号是因为虽然它在逻辑上是一个事件循环,但调用堆栈会不断增加并且永远不会展开:

                                            wait_for_query____...
                                           /
                       handle_query_______/
                      /
wait_for_query_______/

虽然它有效,但它总是将新的堆栈帧添加到堆栈中,最终,在发生足够多的事件后,它会导致 堆栈溢出 错误! (哈哈,太元了)。

我想要的是这样的

                       handle_query           handle_query
                      /            \         /            \
wait_for_query_______/              \_______/              \_____...

到目前为止我得到了什么

我一直听说操作系统只是一个被中断的while(true) 循环,所以(因为我的操作系统最近没有出现 SO 错误)这是我认为好的:

将 main 替换为:

while(1)
    if (wait_for_query() == 0) break;
return 0;
handle_query的返回改为1

但这实际上会提高堆栈效率吗?据我所知,while 循环(以及一般的循环)仍然会以汇编语言生成堆栈帧(因为它们都是使用范围/局部变量/等执行的分支)

很好地管理调用堆栈的 C/C++ 事件循环的最佳实现是什么?

为什么? 基准/比较? 文学/出版物/文档?

注意事项

这个问题假设一个单线程事件循环。并行化的答案也是可以接受的,但我认为一次询问所有问题会有点多;)

开火

【问题讨论】:

while 循环不会随时间增加每次迭代的堆栈使用量。 分支不产生堆栈帧,只产生调用。您的优化器甚至有可能使用分支进行尾调用,以避免堆栈使用,但语言不保证这种优化会发生。 你可能还想看看“Continuation-Passing Style”。 嗯好吧,所以至少我知道 while 循环可以工作并且在堆栈上很好,但它是最好的解决方案吗? @BenVoigt 感谢您的参考! 您想要(C 语言 C++ 语言)或(C 语言 C++ 语言)的解决方案?没有C/C++ 语言。 【参考方案1】:

最初的解决方案从根本上被破坏了。 Event loop 看起来像这样:

while (q != QUITE) 
  q = wait_for_query();
  handle_query(q);

就这么简单。这实际上与您所描述的一致:

它们是一个简单的概念:等待事件、处理事件、等待更多事件。

在初始代码中,从语义上讲,处理事件handle_query() 将永远不会完成,直到所有未来事件也递归完成,这意味着永远不会完成任何事件。这不是你想要的。

细节可能会变得非常复杂,例如您如何获得事件?它是否阻塞?事件是如何分派的? ...等等。

【讨论】:

我有一个关于这个问题的完整 EventLoop 示例:How would you implement a basic event-loop?。【参考方案2】:

实际上,while 本身不会生成任何新的堆栈帧。发出call 指令时会创建堆栈帧。

如果你让handle_query 返回1 就像你提到的那样,那么对于它处理的每个事件,你的循环不会将堆栈增长超过两级 (wait_query+handle_query):

                                  handle_query           handle_query
                                 /            \         /            \
           wait_for_query_______/              \_______/              \_____...
          /
         /
main_loop

这看起来像您正在寻找的堆栈结构。

【讨论】:

以上是关于在调用堆栈上很好的 C/C++ 中的事件循环实现的主要内容,如果未能解决你的问题,请参考以下文章

c语言的堆栈是怎么回事!!

在 C/C++ 中使用 TensorFlow 预训练好的模型—— 间接调用 Python 实现

lua C API

Node.js 事件循环

译并发模型和事件循环

Python中的Gstreamer立即退出,但在命令行上很好