我正在使用Boost.Asio来学习异步操作。在阅读了很多关于这个概念的文章之后,我仍然感到困惑的是为什么这段代码是Asio Docs的C ++ 11例子的一部分并没有使堆栈溢出?我无法想象这个地方代码的流程。它看起来很递归,因为ui-sref
一次又一次地调用自己......我可以想象这个堆栈适用于20个客户端,但适用于2 000个客户端?我认为对于异步操作,将do_accept();
放在循环中并且在其中没有递归调用是更清楚的。它的工作方式是否相同?
我添加了类成员acceptor_.async_accept()
用于测试目的。
代码:
call_nr
答案 0 :(得分:2)
回调!=递归模式。
当异步IO操作完成时,轮询对象会轮询一些控制块,其中包含有关IO操作的详细信息。
在Windows上我们谈论的是OVERLAPPED
结构
在Linux上,我们讨论的是aiocb
结构。
Boost.Asio(以及其他平台,如libuv / node.js,.Net,Java等)继承自该控制块并向其添加其他内容。 Boost.Asio添加了回调对象。
因此,当轮询对象轮询控制块时,Boost.Asio会拉回调并运行它。它可以使用另一个回调启动另一个异步IO操作,但是当原始回调完成时,堆栈将返回给调用者。该功能不会长时间停留 - 即使启动了另一个IO,功能也会继续完成。
流程可能如下所示:
1) execute function A which reads file async
2) an asynchonous IO action is launched with a callback B
3) A continues to execute. it does not block since this is an async IO action
4) A finiehs to run and returns to the caller
5) some time later , the async IO finishes
6) IOCP/epoll polls the control block
7) the IOCP/epoll thread pulls callback B and execute it
8) callback B launches a new async IO action with callback C
9) B continues to execute, since the async IO doesn't block
10) B finished to run and returns to the caller
11) some time later, the Async IO is finished ... and so one
了解更多详情,请阅读 reactor pattern
答案 1 :(得分:1)
因为您正在创建的lambda表达式被保存为仿函数并存储在与io_service
对象关联的数据结构中。
当您调用async_accept时,该调用立即返回。它不会创建一个新的堆栈帧(在语义上。从技术上讲,它确实如此,但它只存在几分之一毫秒)
当最终调用lambda表达式时,它将位于具有io_service
的任何关联线程的堆栈上,而不是原始堆栈。
答案 2 :(得分:1)
只有同步调用才会增加堆栈。
使用acceptor_.async_accept
,您传递的回调函数是要求系统稍后调用 。
重要的是,它不会立即被调用,因此不会从你在堆栈中的位置被调用。
如果你在lambda函数中放置一个断点并检查它的堆栈,你会注意到它是从其他地方调用的(在实现acceptor_
的代码中)。
因此,当它稍后运行并再次调用do_accept
时,它就是新堆栈。
这在传统意义上不是技术上的递归。它只是一个异步的“循环”,它只会反复调用函数,但是每次从一个时间点开始,并且不需要堆栈累积来实现它,所以它可以永远持续下去。 / p>
答案 3 :(得分:0)
我绝不是Boost.Asio的专家。
控制流程如下:
do_accept
被调用。 acceptor_.async_accept
,我们称之为 L 。acceptor_.async_accept
将返回 L 。do_accept
返回。根据连接请求,发生这种情况
do_accept
。从1开始重复。如您所见,第二次调用do_accept
时,前一个调用已经完成。
如果acceptor_.async_accept
在返回之前调用了 L ,那么确实会有一个可能导致堆栈溢出的递归。
您应该查看acceptor_.async_accept
的来源,您应该看到,即使在执行acceptor_.async_accept
期间发生连接请求, L 也不会处理它。
那就是acceptor_.async_accept
和调用 L 的代码之间存在同步机制。