提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
协程和 C++ Boost库的Coroutine2
- 摘要
- 为什么不是boost.coroutine?
- 线程与协程
- 为什么不介绍C++20标准的协程
- C++协程与golang的goroutine
- 二、使用步骤
- 1.引入库
- 2.核心类
- 单个协程
- 两个协程交叉执行
摘要
协程是一种轻量级的并发单位,相比于线程,它具有更小的内存占用和更高效的调度,适用于I/O密集型和计算密集型场景。Boost.Coroutine2是一种协程实现,它是一种特殊控制流,允许在某些位置暂停和恢复执行的子例程。它保留了本地执行状态,并允许重入子例程多次。然而,Boost.Coroutine2的多个实例是顺序执行的,不支持并行执行。在并发性能和资源利用率方面,Goroutine相比于Coroutine2具有更优秀的表现。因此,在编写并发程序时,建议根据实际需求选择适合的并发模型和相应的语言机制。Boost.Coroutine2适用于多个任务的协助和生成器模式等场景。
为什么不是boost.coroutine?
boost官方团队已经声明现已弃用,推荐使用coroutine2。
线程与协程
线程
线程是操作系统提供的一种并发执行的机制,它具有以下特点:
运行在系统态,由操作系统调度。
线程实例之间各自独立运行,共享堆内存,栈空间是各自独占的。
一个初始线程会占用几兆内存,即便它没做什么。
线程之间的切换需要一定的开销。
有完善的同步锁、数据共享机制。
协程
协程是轻量级的并发模型,它具有以下特点:
运行在用户态,由程序员自己调度。
协程实例之间共享堆内存和栈空间。
一个初始协程只占用几KB内存。
协程之间的切换开销极小。
需要开发人员自己实现同步机制。
为什么不介绍C++20标准的协程
C++20引入了无栈协程的特性,为更好的开发异步编程做好了准备。但是由于该版本的协程实现方法一直有很大的争议,协程通过决议的时间很晚,所以实际仅提供了三个关键字来支持协程实现,真正想用协程还要自己实现协程库。然后来不及在C++20中提供协程相关的支持库,像std::generator、std::task等等,需要在C++23中才能提供,有了这些库的支持,基本可以使用标准库的协程了,C++23太新了,编译器的支持当前还没跟上,另外它的实现和使用跟coroutine2差异很小,所以我可能会继续选择使用coroutine2。
C++协程与golang的goroutine
coroutine2跟goroutine不一样,它实际上无法并行
Goroutine是Go语言中轻量级的并发单位,它由Go运行时系统管理和调度,能够在多核处理器上并行执行。而Coroutine2则是一种协程,它是由用户代码自行调度和管理的轻量级线程,通常只在一个线程中顺序执行或者交替,不支持并行执行。
Goroutine相比于Coroutine2具有更小的内存占用和更高效的调度,因此在并发性能和资源利用率方面更加优秀。Go语言通过Goroutine和Channel等机制,提供了一种简洁而高效的并发编程范式,使得开发人员能够轻松地编写并发的程序。
需要注意的是,虽然Coroutine2本身不支持并行执行,但通过一些技术手段可以将Coroutine2组合成并发的执行流,例如使用线程池或者协程池等方式来并发执行Coroutine2。但是这种方式的并发性能和资源利用率相比Goroutine可能会有所下降。
这样比较下看coroutine2对协程的实现好像很鸡肋,但其实goroutine与coroutine行为上的不用也表明了它们的应用场景也不同:
Boost.Coroutine2 可以被视为boost提供的一种特殊控制流,允许在某些位置暂停和恢复执行的子例程。 它保留了本地执行状态,并允许重入子例程多次(如果必须在函数调用之间保持状态,则很有用)。如(生成器、协作式多任务处理)都是它适合的场景,代码结构很简介,这部分优于goroutine。
二、使用步骤
1.引入库
依赖库:Boost.Context
需要语言版:C++11版本
头文件位置:#include <boost/coroutine2/all.hpp>
2.核心类
coroutine2中只有两个核心的类:pull_type,push_type。
在 Boost.Coroutine2 中,协程只能单向传递数据,数据只能单向的从一个代码块流向另一个代码块。流入流出分别对应着 push_type 和 pull_type 类型,由这两个类型组成协程间跳转的通道,同时也是数据传递的通道。
pull_type
pull_type 用于从协程中获取数据,它提供了以下方法:
begin():返回一个迭代器,可以迭代获取协程中的数据。
get():获取协程中的下一个数据。
operator():调用协程的下一个操作,并返回协程是否结束。
push_type
push_type 用于向协程中传递数据,它提供了以下方法:
operator():向协程中传递数据,并返回协程是否结束。
单个协程
/*
* 斐波那契数列
*/
boost::coroutines2::coroutine<int>::pull_type source(
[&](boost::coroutines2::coroutine<int>::push_type& sink) {
int first = 1, second = 1;
sink(first);
sink(second);
for (int i = 0; i<8; ++i) {
int third = first + second;
first = second;
second = third;
sink(third);
}
});
for (auto i : source)
{
std::cout << i << " ";
}
输出:1 1 2 3 5 8 13 21 34 55
两个协程交叉执行
boost::coroutines2::coroutine<int>::pull_type apull([](boost::coroutines2::coroutine<int>::push_type& apush)
{
for (int i = 0; i < 10; i++)
{
cout << "---------------------" << "coroutine 1" << endl;
apush(1);
}
}
);
boost::coroutines2::coroutine<int>::pull_type apull2([](boost::coroutines2::coroutine<int>::push_type& apush)
{
for (int i = 0; i < 10; i++)
{
cout << "---------------------" << "coroutine 2" << endl;
apush(2);
}
}
);
for (int i = 0; i < 10; ++i)
{
apull.get();
apull();
apull2.get();
apull2();
}
cout << "continue>>>" << endl;
输出:
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
---------------------coroutine 1
---------------------coroutine 2
continue>>>