原文
C++11
里也能玩无栈协程了?
答案是:可以!
事实上异网
在很早时,C++11
里就可用无栈协程
写异步代码
了,只不过用起来不太方便
,来看看C++11
里怎么用异网
无栈协程写一个回音服务器
的吧.
#包含 <异网.h++>
#包含 <内存>
#包含 <向量>
#包含 <异网/产生.h++>
用 异网::异步写;
用 异网::缓冲;
用 异网::ip::传控;
用 异网::错误码;
用 大小型;
构 会话:异网::协程
{共针<传控::套接字>套接字_;共针<向量<符>>缓冲_;会话(共针<传控::套接字> 套接字): 套接字_(套接字),缓冲_(新 向量<符>(1024)){}空 符号()(错误码 ec = 错误码(), 大小型 n = 0){如 (!ec) 再入 (本){对 (;;){产生 套接字_->异步读些(缓冲(*缓冲_), *本); 产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本); }}}
};
构 服务器 : 异网::协程
{异网::io服务& io服务_;共针<传控::受者> 受者_;共针<传控::套接字> 套接字_;服务器(异网::io服务& io服务): io服务_(io服务),受者_(新 传控::受者(io服务, 传控::端点(传控::v4(), 54321))){}空 符号()(错误码 ec = 错误码()){再入 (本){对 (;;){套接字_.重置(新 传控::套接字(io服务_));产生 受者_->异步接受(*套接字_, *本);io服务_.提交(会话(套接字_));}}}
};
#包含 <异网/坚定.h++>
整 主()
{异网::io服务 io服务;io服务.提交(服务器(io服务));io服务.跑();
}
先看回声会话
部分的代码:
空 符号()(错误码 ec = 错误码(), 大小型 n = 0){如 (!ec) 再入 (本){对 (;;){产生 套接字_->异步读些(缓冲(*缓冲_), *本); 产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本); }}}
//其中
对 (;;)
{产生 套接字_->异步读些(缓冲(*缓冲_), *本); 产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本);
}
和C++20
的协程代码
是不是很像:
对 (;;)
{协待 套接字_->异步读些(缓冲(*缓冲_), 异网::用可等待); 协待 异步写(*套接字_, 缓冲(*缓冲_, n), 异网::用可等待);
}
所以C++11
里面也是可通过协程
来写异步代码的.
但是异网
的无栈协程
实现比较简陋,使用起来有不少约束.首先要把用无栈协程
的文件
用两个头文件
包装起来,因为内部是宏
实现的,编译完之后需要解定义
.
#包含 <异网/产生.h++>
#包含 <异网/坚定.h++>
第二个不便之处是不自由
,使用协程的对象要从异网::协程
继承,然后定义符号
,注意该符号
是怎么写的:
空 符号()(错误码 ec = 错误码(), 大小型 n = 0){如 (!ec) 再入 (本){对 (;;){产生 套接字_->异步读些(缓冲(*缓冲_), *本); 产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本); }}}
相信你第一次看到该代码时会很懵
,这是个啥意思,再入
是什么鬼,看不懂该代码.不要着急,听我来给你解惑该符号
.
参数
是错误码
和传输大小
,最开始时是默认的,为何这两个参数
呢?注意函数中的再入(本)
,它表明当前该函数
是可重入
的,则何时
会重入呢?
每次产生
返回时就重入
了,产生
之后ec
和大小
就是新的了.比如产生 套接字_->异步读些(缓冲(*缓冲_),*本)
;
返回后,就可得到错误码
和读到的大小
了,所以完整
写法应该
是:
空 符号()(错误码 ec = 错误码(), 大小型 n = 0){如 (!ec) 再入 (本){对 (;;){产生 套接字_->异步读些(缓冲(*缓冲_), *本);//协程重入如(ec){输出<< "读错误消息: " << ec.消息() << "\n";断;}输出 << "读大小: " << n << "\n";产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本); //协程重入如(ec){输出<< "写错误消息: " << ec.消息() << "\n";断;}输出<<"写大小:"<<n<<"\n";}}}
现在看懂了,该怪异
写法了吧.
接着,如果想开启新协程
要怎么做,看服务器接受
的代码:
空 符号()(错误码 ec = 错误码()){再入 (本){对 (;;){套接字_.重置(新 传控::套接字(io服务_));产生 受者_->异步接受(*套接字_, *本);io服务_.提交(会话(套接字_));}}}
在产生受者_->异步接受(*套接字_,*本);
之后通过提交
,把会话
对象丢到io环境
线程池中了,后续会执行该会话
对象,此时,你可能有疑问,为啥可把对象
丢到线程池
里?
答案很简单,因为会话
有符号
它就成为一个函数对象
了.
为什么这里要把会话
函数对象丢到线程池
里呢,为啥不继续产生
呢?因为这里想立即
再次接受
而不是等待会话
结束,如果产生 会话
那就没办法继续接受
了.
通过提交
非阻塞方式调用会话
的协程,两个协程不会相互阻塞
.
除此外,异网
还提供了另外一个创建子协程
的方法:
空 服务器::符号()(异网::错误码 ec, 大小型 长度)
{如 (!ec){再入 (本){干{套接字_.重置(新 传控::套接字(受者_->取执行器()));产生 受者_->异步接受(*套接字_, *本);分叉 服务器(*本)();//`分叉`之后父子协程都会往下执行到此} 当 (是父());产生 套接字_->异步读些(缓冲(*缓冲_), *本); 产生 异步写(*套接字_, 缓冲(*缓冲_, n), *本); }}
}
异网
可通过分叉
来创建子协程,分叉
之后父协程和子协程
都会往下执行,那如何区分
父子协程呢?通过是父()
来区分.
父协程是接受
协程,当走到是父()
时,它会继续接受
,而子协程则会跳出当
循环,往下去读
和写
了.
这就是通过分叉
创建新协程
的方法.
总结
异网
里面有很多有趣的东西,以前还没注意C++11
里用可无栈协程
写异步代码
,也是其余远调用
维护时想用协程去改造,然后发现了该东西,它已有很久的历史了,但我现在才注意到,确实能简化编写异步代码
,后面C++11
版本的其余远调用
会使用异网
无栈协程去写.
不过写法比使用C++20
协程库麻烦多了,但是考虑毕竟是C++11
,能用协程
已不错了,如果能升级到20
标准还是用20
协程舒服.