在asio堆栈协程中直接使用spawn是否安全?

时间:2015-03-21 08:09:22

标签: c++ boost boost-asio coroutine

当我使用spawn在协程中启动一个新的stackfull协程时,valgrind说很多都使用了未初始化的值(valgrind output)。

然后我使用io_service.post调用一个处理程序,并在其中启动一个新的stackfull协程,每件事似乎都没问题。

我已经搜索并阅读了一些文档,但无法找到有关如何在堆栈协程中安全地创建新的堆栈协程的事情。

以下是代码:

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

using namespace std;

int main()
{
    auto use_post = false;
    boost::asio::io_service io_service;
    boost::asio::spawn(io_service,
                       [&io_service, &use_post](boost::asio::yield_context yield){
        if(use_post){
            io_service.post([&io_service]{
                boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield){
                    boost::asio::system_timer timer(io_service);
                    timer.expires_from_now(std::chrono::seconds(1));
                    timer.async_wait(yield);
                    cout << "Sleep 1 second" << endl;
                });
            });
        }
        else{
            boost::asio::spawn(io_service, [&io_service](boost::asio::yield_context yield){
                boost::asio::system_timer timer(io_service);
                timer.expires_from_now(std::chrono::seconds(1));
                timer.async_wait(yield);
                cout << "Sleep 1 second" << endl;
            });
        }
        boost::asio::system_timer timer(io_service);
        timer.expires_from_now(std::chrono::seconds(2));
        timer.async_wait(yield);
        cout << "Sleep 2 seconds" << endl;
    });
    io_service.run();
    return 0;
}

将true设置为use_post变量,将通过post + spawn启动新的stackfull协程。

也许我没有仔细阅读这些文件,但我无法在Boost.Asio C++ Network ProgrammingN4045中找到任何有用的内容,并提升asio文档。

1 个答案:

答案 0 :(得分:4)

这是安全的。

Boost.Asio对Boost.Coroutine的一流支持是一个薄外观,有两个显着的行为:

  • 恢复它的协同程序和处理程序使用strand作为执行上下文。这保证了协程在产生之前不会恢复。
  • 如果检测到没有可用的处理程序可以恢复协同程序,则会阻止协程无限期暂停。发生这种情况时,Boost.Asio将破坏协程,导致挂起的堆栈展开。有关详细信息,请参阅this答案。

在上面的示例代码中,spawn(io_service&)重载导致生成的协程具有自己的strand。因此,如果多个线程正在运行io_service,则每个协程可以并行运行,但不保证这样做。另一方面,如果使用spawn(yield_context)重载,则新协程将具有与调用协程相同的执行上下文(即strand),从而阻止并行执行。