0

0

C++20的协程(coroutine)怎么入门_C++20协程基础语法与入门示例

穿越時空

穿越時空

发布时间:2025-10-25 12:13:02

|

624人浏览过

|

来源于php中文网

原创

C++20协程通过co_await、co_yield、co_return实现暂停与恢复,提升异步代码可读性。1. 协程需定义promise_type控制行为;2. 使用coroutine_handle管理生命周期;3. 通过suspend_always/suspend_never控制执行时机;4. 示例展示协程暂停、手动resume恢复的完整流程。

c++20的协程(coroutine)怎么入门_c++20协程基础语法与入门示例

C++20协程是现代C++中一项重要的异步编程特性,它允许函数在执行过程中暂停并恢复,而无需阻塞线程。与传统回调或基于Future/Promise的异步模型相比,协程让异步代码看起来更像同步代码,提升可读性和维护性。要入门C++20协程,关键不是一开始就理解所有机制,而是先掌握基本语法结构和一个可运行的简单示例。

协程的基本概念与特征

C++20中的协程并不是语言层面新增的一种函数类型,而是通过特定关键字和接口约定实现的。一个函数只要使用了以下三个关键字之一:co_awaitco_yieldco_return,就会被编译器识别为协程。

协程的核心特点是:

  • 可以暂停(suspend)执行,并保存当前状态
  • 之后能从中断点恢复(resume)继续执行
  • 不阻塞调用它的线程(适合I/O等待、定时任务等场景)

但C++20协程是无协程(stackless),这意味着局部变量在挂起期间必须由运行时或框架管理,不能像有栈协程那样直接保存整个调用栈。

立即学习C++免费学习笔记(深入)”;

必需组件:promise_type 与 awaitable

C++20协程需要用户定义一些底层类型来控制其行为,主要包括 promise_type 和满足Awaitable要求的对象。

每一个协程返回类型(比如 Task)都必须有一个嵌套的 promise_type,用于定义协程内部如何初始化、返回值处理以及最终结果获取。

下面是一个最简化的 Task 类型定义:

struct Task {
    struct promise_type {
        Task get_return_object() { return {}; }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() {}
    };
};

解释一下每个部分的作用:

  • get_return_object():协程启动时被调用,返回供外部使用的对象(这里是Task实例)
  • initial_suspend():决定协程开始是否立即挂起。返回 std::suspend_never 表示直接运行
  • final_suspend():协程结束时是否挂起。通常设为永不挂起以便清理资源
  • return_void():用于没有返回值的协程(void类型)
  • unhandled_exception():处理协程内未捕获异常

编写第一个协程示例

现在我们写一个简单的协程函数,演示如何使用 co_return 返回结果:

#include 
#include 

struct Task { struct promise_type { Task get_return_object() { return {}; } std::suspend_never initial_suspend() { return {}; } std::suspend_never final_suspend() noexcept { return {}; } void return_void() {} void unhandled_exception() {} }; };

Task hello_coroutine() { std::cout << "Hello from coroutine!\n"; co_return; // 触发 return_void() }

int main() { auto t = hello_coroutine(); std::cout << "Coroutine was executed.\n"; return 0; }

这个程序输出:

Whimsical
Whimsical

Whimsical推出的AI思维导图工具

下载
Hello from coroutine!
Coroutine was executed.

说明协程立即执行,并未真正“异步”运行。这是因为我们设置了 initial_suspendsuspend_never。如果想延迟执行,可以改为 std::suspend_always,然后手动 resume。

支持 co_await 的简单等待示例

接下来扩展 Task 类型,让它支持 co_await 操作。我们先定义一个简单的awaiter:

auto operator co_await() noexcept {
    struct Awaiter {
        bool await_ready() const noexcept { return true; }
        void await_suspend(std::coroutine_handle<>) noexcept {}
        void await_resume() noexcept {}
    };
    return Awaiter{};
}

然后修改协程函数,加入 await 调用:

Task async_hello() {
    std::cout << "Step 1\n";
    co_await std::suspend_always{}; // 挂起一次
    std::cout << "Resumed - Step 2\n";
    co_return;
}

注意:即使挂起了,也需要有人去调用 handle.resume() 才能继续。完整控制协程生命周期的例子如下:

struct Task {
    struct promise_type;
    using handle_type = std::coroutine_handle;
handle_type coro;

Task(handle_type h) : coro(h) {}

~Task() {
    if (coro) coro.destroy();
}

struct promise_type {
    Task get_return_object() {
        return Task{handle_type::from_promise(*this)};
    }
    std::suspend_always initial_suspend() { return {}; }
    std::suspend_always final_suspend() noexcept { return {}; }
    void return_void() {}
    void unhandled_exception() {}
};

void resume() {
    if (coro && !coro.done())
        coro.resume();
}

};

使用方式:

Task my_coro() {
    std::cout << "Started\n";
    co_await std::suspend_always{};
    std::cout << "After first suspend\n";
    co_await std::suspend_always{};
    std::cout << "Done\n";
}

int main() { auto t = my_coro(); // 不会立刻执行 t.resume(); // 第一次恢复 t.resume(); // 第二次恢复 return 0; }

输出:

Started
After first suspend
Done

这展示了协程真正的暂停与恢复能力。

基本上就这些。C++20协程门槛较高,但只要从最小可运行例子入手,逐步理解 promise_type、handle、suspend 控制逻辑,就能打下坚实基础。后续可学习如何结合事件循环、实现带返回值的Task、生成器(generator)等高级用法。

相关专题

更多
javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

175

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

97

2025.11.27

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1018

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

62

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

400

2025.12.29

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

388

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

571

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

480

2023.08.10

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1.0万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.3万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号