如何使用 Boost.Asio 的 spawn(堆叠协程),使其仅依赖于 Boost.Context?



在Boost 1.80的Asio发行说明中,可以找到:

当针对C++11及更高版本时,spawn((和basic_yield_text是根据Boost.context直接实现的。

耶!现在,我们可以删除对Boost.Coroutine的依赖,它不仅仅是头,而且需要编译(使用boost::asio::spawn时,对Boost.Context的依赖仍然存在(。

现在我的问题是:如何迁移代码,使boost::asio::spawnboost::asio::basic_yield_context只依赖于Boost.Context?

首先,必须定义BOOST_ASIO_DISABLE_BOOST_COROUTINE以禁用Boost.Asio中Boost.Coroutine的所有用法,以避免链接器错误。

定义了BOOST_ASIO_DISABLE_BOOST_COROUTINE后,您将被迫将一个完成令牌作为第三个参数传递给boost::asio::spawn。此完成令牌必须提供一个具有签名void handler(std::exception_ptr)的处理程序,其中std::exception_ptr用于传输从协程中抛出的异常。

如果您只是想丢弃协程中的异常(或者如果异常不可能从协程中泄漏出来(,则可以将boost::asio::detached作为spawn完成令牌传递。例如:

boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
// Do coroutine stuff
},
boost::asio::detached
);

如果您希望boost::asio::io_context::run重新抛出异常(就像没有完成令牌的旧行为一样(,您应该传递以下处理程序(或等效程序(作为完成令牌:

[](std::exception_ptr e) { if (e) std::rethrow_exception(e); }

例如:

boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
// Do coroutine stuff
throw std::runtime_error("Oops");
},
[](std::exception_ptr e) { if (e) std::rethrow_exception(e); }
);

请注意,Boost 1.80中目前存在一个错误,boost::asio::spawn无法正确捕获从协同程序抛出的异常。

下面是一个完整的例子,演示了禁用Boost.Coroutine,使其仅依赖于Boost.Context(和Boost.System(:

#define BOOST_ASIO_DISABLE_BOOST_COROUTINE
#include <exception>
#include <iostream>
#include <stdexcept>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
//------------------------------------------------------------------------------
struct propagating_t
{
constexpr propagating_t() = default;
void operator()(std::exception_ptr e) const
{
if (e) std::rethrow_exception(e);
}
};
// Our own custom completion token that rethrows exceptions leaked
// from coroutines.
constexpr propagating_t propagating;
//------------------------------------------------------------------------------
int main()
{
boost::asio::io_context ioctx;
boost::asio::spawn(
ioctx,
[](boost::asio::yield_context yield)
{
std::cout << "Coroutine start" << std::endl;
throw std::runtime_error("bad");
},
propagating
);
try
{
ioctx.run();
}
catch (const std::exception& e)
{
std::cout << "Caught exception: " << e.what() << std::endl;
}
catch (...)
{
std::cout << "Caught unknown exception" << std::endl;
}
return 0;
}

特别感谢《Asio》的作者Chris Kohlhoff,他帮助我理解了boost::asio::spawn的用法。

最新更新