深入理解 C++ 中的协程(Coroutines):概念与实用指南

2024-08-30 09:28

本文主要是介绍深入理解 C++ 中的协程(Coroutines):概念与实用指南,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

深入理解 C++ 中的协程(Coroutines):概念与实用指南

引言

在现代编程中,异步编程和并发处理变得越来越重要。C++20 引入了协程(coroutines)这一特性,使得编写异步代码变得更加简单和直观。协程允许函数在执行过程中暂停并在稍后恢复,从而实现非阻塞的异步操作。本文将深入探讨 C++ 中的协程,包括其基本概念、使用方法以及实际应用示例。

什么是协程?

协程是一种特殊类型的函数,它可以在执行过程中被挂起(suspend)和恢复(resume)。与传统的函数不同,协程可以在多个点之间暂停执行,并在需要时继续执行。这使得协程非常适合处理异步操作,例如网络请求、文件 I/O 等。

协程的特点

  1. 挂起与恢复:协程可以在执行过程中挂起,并在稍后恢复执行。
  2. 状态保持:协程在挂起时可以保持其状态,包括局部变量的值。
  3. 非阻塞:协程允许其他代码在等待期间继续执行,从而实现非阻塞的异步编程。

C++ 中的协程基础

在 C++20 中,协程的实现依赖于几个关键的概念和关键字:

  1. co_await:用于挂起协程的执行,等待某个异步操作完成。
  2. co_return:用于返回协程的结果,并结束协程的执行。
  3. co_yield:用于生成一个值并挂起协程的执行,允许协程在多个点之间返回值。

协程的基本结构

一个简单的协程示例如下:

#include <iostream>
#include <coroutine>struct SimpleCoroutine {struct promise_type {SimpleCoroutine get_return_object() {return SimpleCoroutine{};}std::suspend_never initial_suspend() { return {}; }std::suspend_never final_suspend() noexcept { return {}; }void unhandled_exception() { std::terminate(); }void return_void() {}};using handle_type = std::coroutine_handle<promise_type>;handle_type handle;SimpleCoroutine(handle_type h) : handle(h) {}~SimpleCoroutine() { handle.destroy(); }
};SimpleCoroutine myCoroutine() {std::cout << "Start Coroutine" << std::endl;co_return; // 结束协程
}int main() {auto coroutine = myCoroutine();return 0;
}

在这个示例中,我们定义了一个简单的协程 myCoroutine,它在开始时打印一条消息,然后结束。promise_type 结构体定义了协程的行为,包括如何处理返回值和异常。

使用协程

1. 创建协程

创建协程的第一步是定义一个 promise_type,它负责管理协程的状态和返回值。然后,使用 co_awaitco_returnco_yield 关键字来控制协程的执行。

2. 挂起与恢复

使用 co_await 可以挂起协程的执行,等待某个异步操作完成。例如,假设我们有一个异步操作 asyncOperation,我们可以这样使用协程:

#include <iostream>
#include <coroutine>
#include <thread>
#include <chrono>struct Awaitable {bool await_ready() const noexcept { return false; }void await_suspend(std::coroutine_handle<> h) const {std::thread([h]() {std::this_thread::sleep_for(std::chrono::seconds(1)); // 模拟异步操作h.resume(); // 恢复协程}).detach();}void await_resume() const noexcept {}
};struct Coroutine {struct promise_type {Coroutine get_return_object() {return Coroutine{std::coroutine_handle<promise_type>::from_promise(*this)};}std::suspend_never initial_suspend() { return {}; }std::suspend_never final_suspend() noexcept { return {}; }void unhandled_exception() { std::terminate(); }void return_void() {}};using handle_type = std::coroutine_handle<promise_type>;handle_type handle;Coroutine(handle_type h) : handle(h) {}~Coroutine() { handle.destroy(); }void resume() {handle.resume();}
};Coroutine myCoroutine() {std::cout << "Coroutine started, waiting for async operation..." << std::endl;co_await Awaitable(); // 挂起协程,等待异步操作完成std::cout << "Coroutine resumed after async operation!" << std::endl;
}int main() {auto coroutine = myCoroutine();coroutine.resume(); // 启动协程std::this_thread::sleep_for(std::chrono::seconds(2)); // 等待协程完成return 0;
}

在这个示例中,Awaitable 结构体实现了一个简单的异步操作。myCoroutine 在执行时会挂起,等待 Awaitable 完成,然后恢复执行。

3. 返回值与生成器

协程不仅可以返回值,还可以生成多个值。使用 co_yield 可以在协程中生成值并挂起执行。例如,下面的示例展示了如何使用协程生成 Fibonacci 数列:

#include <iostream>
#include <coroutine>struct Fibonacci {struct promise_type {int current = 0;int next = 1;Fibonacci get_return_object() {return Fibonacci{std::coroutine_handle<promise_type>::from_promise(*this)};}std::suspend_always initial_suspend() { return {}; }std::suspend_always final_suspend() noexcept { return {}; }void unhandled_exception() { std::terminate(); }int yield_value(int value) {current = value;return value;}void return_void() {}};using handle_type = std::coroutine_handle<promise_type>;handle_type handle;Fibonacci(handle_type h) : handle(h) {}~Fibonacci() { handle.destroy(); }bool move_next() {handle.resume();return !handle.done();}int current_value() {return handle.promise().current;}
};Fibonacci generate_fibonacci() {int a = 0, b = 1;while (true) {co_yield a; // 生成当前值int next = a + b;a = b;b = next;}
}int main() {auto fib = generate_fibonacci();for (int i = 0; i < 10; ++i) {if (fib.move_next()) {std::cout << fib.current_value() << " ";}}std::cout << std::endl;return 0;
}

在这个示例中,generate_fibonacci 协程生成 Fibonacci 数列的值。每次调用 co_yield 时,协程会返回当前值并挂起,直到下一次调用 move_next

实际应用场景

1. 网络编程

协程在网络编程中非常有用,可以轻松处理多个并发连接而不需要复杂的线程管理。例如,使用协程可以实现一个简单的 HTTP 服务器,处理多个请求而不阻塞主线程。

2. 游戏开发

在游戏开发中,协程可以用于处理游戏逻辑、动画和事件。通过使用协程,可以实现更清晰的代码结构,避免回调地狱。

3. 数据处理

在数据处理和流处理场景中,协程可以用于处理大数据集,允许在处理过程中暂停和恢复,从而提高效率。

总结

C++20 中的协程为异步编程提供了一种新的方式,使得编写非阻塞代码变得更加简单和直观。通过使用 co_awaitco_returnco_yield,开发者可以轻松实现异步操作、生成器和状态机等功能。随着 C++ 协程的广泛应用,开发者可以更高效地处理并发任务,提高代码的可读性和可维护性。

希望本文能帮助你更好地理解 C++ 中的协程,并在实际项目中有效地应用这一强大特性!如果你有任何问题或想要深入讨论的内容,请随时联系我。

这篇关于深入理解 C++ 中的协程(Coroutines):概念与实用指南的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1120426

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言