asio中用到了两种coroutine,一个是asio作者自己写的stackless coroutine,一个是封装了Boost.Coroutine的spawn,Boost.Coroutine是boost库中的一个stackful coroutine。其实这两个的主要区别我觉得就是stackless的不能使用局部变量,但是速度较快;stackful的可以使用局部变量,但是速度可能不如stackless的。
首先说一下coroutine是个什么玩意,其实很多语言中都有这个东西,个人感觉这个玩意就像hyq童鞋说的那样,就是个带状态的函数,每次调用执行不同的功能。先上一下stackless的coroutine的简单示例:
#include <iostream>
#include <boost/asio/yield.hpp>
int foo(boost::asio::coroutine& ct)
{
std::cout << "before reenter" << std::endl;
reenter(ct)
{
std::cout << "before yield1" << std::endl;
yield std::cout << "yield1" << std::endl;
std::cout << "before yield2" << std::endl;
yield return 1;
}
std::cout << "after reenter" << std::endl;
return 2;
}
int main(int argc, char* argv[])
{
boost::asio::coroutine ct;
while (!ct.is_complete())
{
int ret = foo(ct);
std::cout << "return:" << ret << std::endl;
}
return 0;
}
运行结果:
简单来说就是首先会执行reenter之前的代码(打印before reenter),当函数运行到reenter范围内的时候,第一次会执行第一个yield之前的代码(打印before yield1)和yield关键词之后跟的那个语句(打印 yield1),然后直接跳出reenter范围执行之后的代码(打印after reenter和return 2);第二次调用时进入reenter后则会直接从第一个yield下面开始执行,执行到第二个yield结束,因为yield后面接的是一个return语句,所以reenter范围后面的代码没有执行;当没有yield可以继续执行的时候,就不再进入reenter了。
灰常简单,其实就是个switch case,我都觉得自己用switch case实现个肯定不比这个差。不过这几天学习asio下来发现,asio本身根本就没啥可学习的高级的东西,都是在学他的思想,虽然只是这么简单的一段代码,换个思维方式可能就会有很大作用,一般在异步程序中使用coroutine就是为了少写一些回调。如果一个协议里面要有顺序的读写几十次的话,对于同步的操作来说很简单,一路按照协议read、write、read、write下来就行了,但是异步的就不行了,你必须在上一个read或者write完成的handler中发起下一次的read或者write,协议稍微一复杂,回调函数就会多得让你恶心,这个时候coroutine就派上用场了,只需要一个回调函数,只要不停的调用就好了,函数会自动的按照你写得yield的顺序执行相应的代码,让代码看起来就像是同步的一样。上一下代码,还是异步的echo server:
// asio_sample.cpp : 定义控制台应用程序的入口点。
//
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/asio/yield.hpp>
#include <iostream>
boost::asio::io_service ios;
boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), 1234);
boost::asio::ip::tcp::acceptor acceptor(ios, endpoint);
char data[1024];
void handle(boost::asio::coroutine ct,boost::asio::ip::tcp::socket* sock,
const boost::system::error_code& error,size_t bytes_transferred)
{
if (error)
{
std::cout << "read发生错误:" << error.message() << std::endl;
delete sock;
return;
}
reenter(ct)
{
yield boost::asio::async_write(*sock, boost::asio::buffer(data, bytes_transferred),
boost::bind(handle,ct, sock,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
yield sock->async_read_some(boost::asio::buffer(data),
boost::bind(handle, boost::asio::coroutine(),sock,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}
void session(boost::asio::ip::tcp::socket* sock,
const boost::system::error_code& error)
{
if (error)
{
delete sock;
}
else
{
std::cout << "有客户端连接" << std::endl;
sock->async_read_some(boost::asio::buffer(data),
boost::bind(handle, boost::asio::coroutine(),sock,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
boost::asio::ip::tcp::socket* s = new boost::asio::ip::tcp::socket(ios);
acceptor.async_accept(*s, boost::bind(session, s,
boost::asio::placeholders::error));
}
int main(int argc, char* argv[])
{
boost::asio::ip::tcp::socket* s = new boost::asio::ip::tcp::socket(ios);
acceptor.async_accept(*s, boost::bind(session, s, boost::asio::placeholders::error));
ios.run();
return 0;
}
运行结果就不贴了,因为功能跟asio学习笔记5中的那个一模一样.这里每次收到收据都要把它写回去,本来需要两个handler,但是用了yiled之后就可以只需要一个了。
另外需要说一下boost::asio::coroutine可以作为一个基类,然后在子类的函数中使用reenter(this)就可以了,但是我还是比较喜欢把它作为一个成员变量或者参数,这个看个人喜好了,asio的http server4示例就是用stackless coroutine写的,想研究的童鞋可以看看去。