1|0一些实现的c++协程
C++协程实现相关视频讲解:(视频代码资料点击 正在跳转 获取)
协程是一种函数对象,可以设置锚点做暂停,然后再该锚点恢复继续运行,我觉得这是最合适的定义,用户态线程,轻量级线程,可中断恢复的函数,这些都不够精确,先来认识一个boost 1.75的一个例子
#include <iostream>
#include <boost/coroutine2/all.hpp>
void coroutine_function(boost::coroutines2::coroutine<void>::pull_type & coro_back)
{
std::cout << "a ";
coro_back(); // 锚点,返回
std::cout << "b ";
coro_back(); //锚点 返回
std::cout << "c ";
}
int main()
{
boost::coroutines2::coroutine<void>::push_type coroutine_object(coroutine_function); // 创建协程
std::cout << "1 ";
coroutine_object(); // 运行协程
std::cout << "2 ";
coroutine_object(); // 返回锚点,继续运行协程
std::cout << "3 ";
coroutine_object(); // 返回锚点,继续运行协程
return 0;
}
g++ test.cpp -lboost_coroutine -lboost_context -o test
./pull
--------------输出分割线-------------
1 a 2 b 3 c
在main( )中创建了一个协程 coroutine_object,然后调用coroutine_object()去运行,实际上运行的coroutine_function( )函数,而且每次运行到coro_back();就中断当前的执行返回,下次调用coroutine_object()就从这个断点继续运行,这就是协程的全部了
为什么会有协程是轻量级线程的说法呢?因为协程具有中断可恢复的特性,那么只需要在开一个全局的数组存储所有的协程,在协程中断时,不断轮转调用下一个协程继续运行即可; 这看起来似乎和线程无异,但其实有巨大的区别,因为协程本质是函数,调用协程后原来的地方就会被阻塞,协程处理完了才返回结果,这是天然同步的,而多线程无法做到这点,因为多线程的调度受内核控制,触发点来自于硬件时钟中断不可预见,同时又运行在多核心下,调用后运行次序是不确定的,想实现同步调用就必须通过std::promise/future 去辅佐,但为了性能往往见到的是异步+回调的方式进行多线程的交互,异步回调代码的可读性是很差的而且还需要考虑一大堆并发上锁的情况,协程因其函数本质,是天然同步的,而在遇到阻塞条件时候,把cpu让给别的协程,等条件满足了再通过中断可恢复的特性再继续运行,就实现了并发,同步+并发就是协程强大的地方,其使用范式和轮转+同步非阻塞很像
接下来会介绍一些目前的实现的协程,有非官方的: boost.coroutine2的协程,使用起来方便,让我们可以直观了解协程;微信的libco, 源码很好阅读,资料多,可以进一步学习到协程是如何实现运行的;而官方本身的c++20协程,还不成熟,使用起来比较复杂,官方的东西还是需要提前了解;
C/C++ Linux服务器开发高级架构学习视频点击观看:C/C++Linux服务器开发/Linux后台架构师-学习视频
2|0一些实现的c++协程
2|1boost中的协程
push_type和pull_type
boost自己早就实现了一套协程,先后推出了两个版本boost coroutine和boost coroutine2,现在第一个版本boost coroutine已经弃用, 直接看看coroutin2的简单例子
#include <iostream>
#include <boost/coroutine2/all.hpp>
void foo(boost::coroutines2::coroutine<int>::push_type & sink)
{
std::cout<<"start coroutine\n";
sink(1);
std::cout<<"finish coroutine\n";
}
int main()
{
boost::coroutines2::coroutine<int>::pull_type source(foo);
std::cout<<source.get()<<std::endl;
std::cout<<source()<<std::endl;
std::cout<<"finish\n";
return 0;
}
编译链接运行后
g++ pull.cpp -lboost_coroutine -lboost_context -o pull
./pull
--------------输出分割线-------------
start coroutine
1
finish coroutine
finish
boost.corountine2中的协程增加了push_type和pull_type用于提供协程数据的流转,约束了数据的从push_type流入,从pull_type流出, 上面的demo定义协程对象source的时候使用了pull_type,所以协程函数参数类型是push_type
当协程对象被创建之后就直接运行,直到sink(1)的时候暂停返回