1. 前置知识
C++协程编程需要一些前置知识,如C++11及以上版本,Lambda表达式的使用和理解,以及函数对象、函数指针等的概念。
2. C++中的协程
2.1 协程的概念
协程(Coroutines)是一种特殊的子例程,可以在某个位置挂起执行,并在之后的某个时刻恢复执行。与线程有所区别,在程序执行过程中,协程能够挂起其自身的执行上下文,然后切换到另外一个执行上下文中,等到指定时机再次回到原来的上下文中,继续执行代码。
协程的执行上下文包含了程序计数器和栈,协程使用其自身的栈来保存状态。多个协程可以共享同一个堆(Heap)。
2.2 协程的优点
协程比线程更轻量级,协程只需要一次函数调用和一些存储来实现上下文切换,而线程则需要操作系统进行上下文切换。在分布式系统和高并发系统中,使用协程可以显著提高性能。
2.3 协程的使用
C++20标准中引入了协程库(Coroutine Library),为了使用协程,需要包含头文件
下面是一个简单的协程示例:
#include
#include
struct MyCoro {
struct promise_type {
int current_value;
auto get_return_object() { return MyCoro{std::coroutine_handle::from_promise(*this)}; }
auto initial_suspend() { return std::suspend_never{}; }
auto final_suspend() noexcept { return std::suspend_never{}; }
void return_void() {}
auto yield_value(int value) { current_value = value; return std::suspend_always{}; }
void unhandled_exception() {}
};
std::coroutine_handle _coroutine;
bool move_next() {
_coroutine.resume();
return not _coroutine.done();
}
int current_value() { return _coroutine.promise().current_value; }
~MyCoro() { if (_coroutine) _coroutine.destroy(); }
};
MyCoro counter(int start, int step)
{
for(int i = start;; i += step)
co_yield i;
}
int main()
{
MyCoro c = counter(0, 1);
while(c.move_next()) std::cout << c.current_value() << std::endl;
return 0;
}
注:此代码示例来源自CSDN博客,原文链接:https://blog.csdn.net/zgsmile/article/details/107683687。
以上代码使用了一个简单的计数器,每次迭代一个值,使用co_yield指令暂停运行,并返回一个值。
协程函数例如counter返回MyCoro类型,并通过迭代器的方式使用move_next和current_value函数来使用异步协程计数器。
2.4 协程的实现
C++协程的实现,可以通过两种方式来完成:
使用伪协程(Coroutine):伪协程依赖于语言之外的机制来实现。学习和使用起来会比较困难,但可移植性较强。
使用生成器(Generators):这些可在C++的迭代协议及协程类型等标准库组件的帮助下使用。
伪协程的实现比较复杂,本文不再介绍,感兴趣的读者可以查阅相关资料学习。
接下来本文将介绍协程的一种实现方式——生成器。
3. C++中的生成器
3.1 生成器的概念
生成器是一种特殊的协程,其中协程函数返回序列值而不是函数体内的某个特定值。
3.2 生成器的实现
生成器被用来创建指向序列的迭代器。建议使用range-v3库中提供的生成器组件进行开发。以下是一个生成器的示例:
#include
#include
#include
int main() {
int constexpr max_val(100);
auto const gen = ranges::views::generate([val = int{}]() mutable -> std::optional {
return ++val <= max_val ? val : std::nullopt;
});
for (int rp::val : gen) { // RP的解释:此处的rp是我的声明,表达rupeng过程的想法。val则是rp经过前面的宏展开列出来的属性
std::cout << rp::val << " ";
}
std::cout << std::endl;
return 0;
}
注:此代码示例来源自个人博客,原文链接:https://rupeng.blog.csdn.net/article/details/106738860。
以上代码使用range-v3库中的views::generate函数,将生成器函数和一个闭包一起提供,返回一个view类型。调用者可以使用range-for语法循环访问template-generated等同于std::optional
在generate函数中,使用std::optional
3.3 生成器的用途
生成器实现了对序列的非可变视图,这在生成大量数据的时候是非常有用的。此外,生成器可以与其他STL组件配合使用,例如filter和transform等。
4. 总结
C++中的协程编程和生成器可以帮助提高程序的性能,协程是异步编程的一种方式,应用于高并发和分布式系统场景中。生成器可以方便地生成给定序列的视图,避免生成大量数据的过程,减小内存开销。