【问题标题】:Can I use a stackful coroutine as the wait handler of a steady_timer which is defined inside the very stackful coroutine?我可以使用 stackful coroutine 作为在非常 stackful coroutine 中定义的 stable_timer 的等待处理程序吗?
【发布时间】:2014-02-14 05:39:39
【问题描述】:

我可以通过以下方式使用stackful coroutine和boost::asio::steady_timer::async_wait吗? 关键是(我的理解,不确定)在等待期间,局部变量 timer 不在堆栈上,因此无法访问。那么回调能否正常进行呢? (仅供参考,它在我使用 clang++5.0 的 Mac 上运行良好。)

boost::asio::io_service io;
void Work(boost::asio::yield_context yield) {
  boost::asio::steady_timer timer(io);

  timer.expires_from_now(std::chrono::seconds(5));
  timer.async_wait(yield);

  cout << "Woke up." << endl;
}

int main() {
  boost::asio::spawn(io, Work);
  io.run();
  return 0;
}

我觉得这个问题值得比较一下:boost asio deadline_timer

【问题讨论】:

    标签: c++ boost boost-asio


    【解决方案1】:

    是的,将boost::asio::yield_context 传递给在同一个协程中具有自动存储持续时间的对象是安全的。

    Boost.Coroutine 使用Boost.Context 执行上下文切换。 Boost.Context 提供了暂停当前执行路径、保留栈(包括Work()timer 等局部变量)和转移执行控制的方法,允许同一个线程以不同的栈运行。因此,boost::asio::steady_timer 对象具有自动存储期限,其生命周期将在以下任一情况下结束:

    • 控制通过return 退出由Work() 指定的块、到达函数末尾或展开堆栈的异常。
    • 关联的io_service 被销毁。内部处理程序维护协程的共享所有权,当io_service 被销毁时,所有关联的处理程序也被销毁。这种破坏将导致 Boost.Coroutine 强制每个协程的堆栈展开。

    boost::asio::spawn() 被调用时,Boost.Asio 执行一些设置工作,然后将dispatch() 一个内部处理程序,它将使用用户提供的函数作为入口点创建一个协程。当yield_context 对象作为处理程序传递给异步操作时,Boost.Asio 将在使用完成处理程序启动异步操作后立即yield,该完成处理程序将复制结果并恢复协程。协程拥有的strand 用于保证yield 发生在resume 之前。下面尝试说明示例代码的执行:

    boost::asio::io_service io_service;
    boost::asio::spawn(io_service, &Work);
    `-- dispatch a coroutine creator
        into the io_service.
    io_service.run();
    |-- invoke the coroutine creator
    |   handler.
    |   |-- create and jump into
    |   |   into coroutine         ----> Work()
    :   :                                |-- timer created
    :   :                                |-- setting timer expiration
    :   :                                |-- 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         :   :
    |   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 << "Waked up." << endl;
    :   :                                |-- exiting Work() block, timer is 
    :   :                                |   destroyed.
    |   `-- return                 <---- `-- coroutine done, yielding
    `-- no outstanding work in 
        io_service, return.
    

    【讨论】:

    • @updogliu 执行可以在单个线程内的多个协程之间转移。有关示例,请参阅 motivation 的 Boost.Coroutine。
    • Coroutine 的堆栈的展开(因为io_service 被破坏或其他原因)是否也包裹在Coroutine 执行的strand 中?
    • @updogliu 当控制通过return 或到达函数末尾退出块时,协程的堆栈在处理程序的strand 的上下文中展开。当io_service 被销毁时,协程的堆栈在strand 之外展开。对于协程,strand 的主要目的是保证 yieldresume 被调用之前完成。
    【解决方案2】:

    是的 - 它应该工作。 boost::asio::steady_timer timer(io) 在 io-service 上注册定时器。

    【讨论】:

    • 您的意思是在调用async_wait 之后不再需要timer 吗?
    猜你喜欢
    • 2014-11-25
    • 1970-01-01
    • 1970-01-01
    • 2022-10-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-04
    • 2019-08-15
    相关资源
    最近更新 更多