boost::asio::spawn 是做什么的?

What does boost::asio::spawn do?

我无法在脑海中形成控制流如何随 spawn 发生的画面。

  1. 当我调用 spawn(io_service, my_coroutine) 时,它是否会向 io_service 队列添加一个新的处理程序来包装对 my_coroutine 的调用?

  2. 当我在协程中调用一个异步函数并将其传递给我时 yield_context,它是否会暂停协程直到异步操作完成?

    void my_coroutine(yield_context yield)
    {
      ...
      async_foo(params ..., yield);
      ...   // control comes here only once the async_foo operation completes
    }

我不明白的是我们如何避免等待。假设 my_coroutine 服务于 TCP 连接,当特定实例挂起等待 async_foo 完成时,如何调用 my_coroutine 的其他实例?

简而言之:

  1. When spawn() is invoked, Boost.Asio performs some setup work and then will use a strand to dispatch() 内部处理程序使用用户提供的函数作为入口点创建协程。在某些情况下,内部处理程序可以在对 spawn() 的调用中被调用,而其他时候它将被发布到 io_service 以进行延迟调用。
  2. 协程被挂起,直到操作完成并调用完成处理程序,io_service 被销毁,或者 Boost.Asio 检测到协程已被挂起且无法恢复,此时 Boost.Asio 将销毁协程。

如上所述,当 spawn() 被调用时,Boost.Asio 执行一些设置工作,然后将使用 stranddispatch() 创建协程的内部处理程序使用用户提供的功能作为入口点。当 yield_context 对象作为处理程序传递给异步操作时,Boost.Asio 将在使用完成处理程序启动异步操作后立即 yield 将复制结果和 恢复协程。前面提到的 strand 由协程拥有,用于保证 yield 发生在 resume 之前。让我们考虑一个简单的例子 demonstrating spawn():

#include <iostream>
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>

boost::asio::io_service io_service;

void other_work()
{
  std::cout << "Other work" << std::endl;
}

void my_work(boost::asio::yield_context yield_context)
{
  // Add more work to the io_service.
  io_service.post(&other_work);

  // Wait on a timer within the coroutine.
  boost::asio::deadline_timer timer(io_service);
  timer.expires_from_now(boost::posix_time::seconds(1));
  std::cout << "Start wait" << std::endl;
  timer.async_wait(yield_context);
  std::cout << "Woke up" << std::endl;    
}

int main ()
{
  boost::asio::spawn(io_service, &my_work);
  io_service.run();
}

上面的例子输出:

Start wait
Other work
Woke up

这里试图说明例子的执行。 |中的路径表示活动栈,:表示挂起栈,用箭头表示控制权转移:

boost::asio::io_service io_service;
boost::asio::spawn(io_service, &my_work);
`-- dispatch a coroutine creator
    into the io_service.
io_service.run();
|-- invoke the coroutine creator
|   handler.
|   |-- create and jump into
|   |   into coroutine         ----> my_work()
:   :                                |-- post &other_work onto
:   :                                |   the io_service
:   :                                |-- create timer
:   :                                |-- set timer expiration
:   :                                |-- cout << "Start wait" << endl;
:   :                                |-- timer.async_wait(yield)
:   :                                |   |-- create error_code on stack
:   :                                |   |-- initiate async_wait operation,
:   :                                |   |   passing in completion handler that
:   :                                |   |   will resume the coroutine
|   `-- return                 <---- |   |-- yield
|-- io_service has work (the         :   :
|   &other_work and async_wait)      :   :
|-- invoke other_work()              :   :
|   `-- cout << "Other work"         :   :
|       << endl;                     :   :
|-- io_service still has work        :   :
|   (the async_wait operation)       :   :
|   ...async wait completes...       :   :
|-- invoke completion handler        :   :
|   |-- copies error_code            :   :
|   |   provided by service          :   :
|   |   into the one on the          :   :
|   |   coroutine stack              :   :
|   |-- resume                 ----> |   `-- return error code
:   :                                |-- cout << "Woke up." << endl;
:   :                                |-- exiting my_work block, timer is 
:   :                                |   destroyed.
|   `-- return                 <---- `-- coroutine done, yielding
`-- no outstanding work in 
    io_service, return.