boost :: asio :: spawn有什么作用?

时间:2015-05-31 12:44:43

标签: c++ boost boost-asio boost-coroutine

我无法形成关于控制流如何与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
    }
  3. 我不明白的是我们如何避免等待。假设my_coroutine服务于TCP连接,在特定实例被暂停时,如何调用my_coroutine的其他实例,等待async_foo完成?

1 个答案:

答案 0 :(得分:23)

简而言之:

  1. 调用spawn()时,Boost.Asio执行一些设置工作,然后使用strand dispatch()内部处理程序,使用用户提供的函数作为条目创建协同程序点。在某些情况下,可以在对spawn()的调用中调用内部处理程序,有时将其发布到io_service以进行延迟调用。
  2. 协程被挂起,直到操作完成并调用完成处理程序,io_service被销毁,或者Boost.Asio检测到协程已被挂起而无法恢复它,此时Boost .Asio会破坏协程。
  3. 如上所述,当调用spawn()时,Boost.Asio会执行一些设置工作,然后使用strand dispatch()内部处理程序,使用提供的用户创建协同程序作为切入点。当yield_context对象作为处理程序传递给异步操作时,Boost.Asio将在使用完成处理程序启动异步操作后立即产生,该处理程序将复制结果并且 resume 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.