Boost.asio中stackful协程是由Boost::Asio::spawn开启的,文档说,spawn是协程的一种高层次的封装,spawn由许多版本,多用以下定义:
template<
typename Function>
void spawn(
const boost::asio::io_context::strand & s,
Function && function,
const boost::coroutines::attributes & attributes = boost::coroutines::attributes());
function定义中用到了yield_context,从名字看是协程执行时的上下文,对其描述的相关文档不多,在异步操作函数中,它可以代替handler函数,可以有两种方式来理解其效果:
- 运行到非阻塞函数(向io_context提交任务,这里为方便用A表示,运行任务A),返回,即跳出该函数,记录为代码点1,将控制权交给io_context,io_context的任务队列中如有其他任务(为方便用B表示),则执行任务B,当任务A成后,重新进入代码点1的函数,接着代码点1继续运行
- 虽然是非阻塞函数(向io_context提交任务,这里为方便用A表示,运行任务A),但“阻塞”了当前执行的代码(用代码点1表示),将控制权交给io_context,io_context的任务队列中如有其他任务(为方便用B表示),则执行任务B,当“阻塞”的任务完(任务A)成后,接着“阻塞”的代码(代码点1)继续运行。
以上方式1就是典型的协程方式,方式2则更“直白“些。
作为对比,如果不采用协程,而用异步回调函数的话,以上的运行顺序可能是:提交任务A,继续执行代码,执行任务A或B,也就是说,代码点1之后的代码是先于任务A完成之前运行,它们之间并没有如代码顺序那样先后运行。
可以通过以下的示例来理解上面的运行顺序。
void other_work()
{
std::cout << "Other work" << std::endl;
}
void echo(boost::asio::io_context &io_context, boost::asio::yield_context context)
{
io_context.post(&other_work);
boost::system::error_code ec;
boost::asio::ip::tcp::endpoint endp(boost::asio::ip::address_v4::from_string("127.0.0.1"),8000);
boost::asio::ip::tcp::socket sock(io_context);
sock.async_connect(endp,context);
std::cout << "connect ends" << std::endl;
char *request="GET / HTTP/1.1\r\nAccept: */*\r\n\r\n";
sock.async_write_some(boost::asio::buffer(request, strlen(request)),context[ec]);
std::cout << "write ends" << std::endl;
char data[10096];
memset(data,0,sizeof(data));
size_t len=sock.async_read_some(boost::asio::buffer(data, sizeof(data)), context[ec]);
std::cout << "read ends: " <<len<< std::endl;
std::cout << data << std::endl;
sock.close(); }
void demo1()
{
boost::asio::io_context io_service;
spawn(io_service, [&](boost::asio::yield_context context)
{
io_service.post(&other_work);
boost::asio::deadline_timer timer(io_service);
timer.expires_from_now(boost::posix_time::seconds(10));
std::cout << "Start wait" << std::endl;
timer.async_wait(context);
std::cout << "Woke up" << std::endl;
});
io_service.run();
}
static void demo2()
{
boost::asio::io_context io_context;
spawn(io_context, [&io_context](boost::asio::yield_context context)
{
echo(io_context,context);
});
io_context.run();
}
Demo1的运行结果
Start wait
Other work
Woke up
Demo2的运行结果
connect ends
write ends
read ends: … 以下是字节数和内容
可以看出,上面运行的异步函数似乎变成同步(阻塞)了,在“阻塞”期间,控制权在io_context(其实是操作系统)手中,还可以运行其他任务。
以上两个示例仅用于演示,未考虑其他因素。
【目前版本spwan(boost v1.67)用到了Boost.Coroutine,而Boost.Coroutine已被标记为deprecated,导致编译warning,Boost.Coroutine又用到了Boost.Context,Boost.Context才是保存运行环境用到的库,它与操作系统相关,所以要使示例能够编译成功,需要已编译好的Boost.Coroutine,Boost.Thread,Boost.Context三个库,且注意它们在连接命令中出现的顺序。Boost.Coroutine2时,仅依赖,Boost.Context了。】