news 2026/8/26 16:00:03

C++20 Coroutine 与 Golang Goroutine:一场关于并发未来的深度对话

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++20 Coroutine 与 Golang Goroutine:一场关于并发未来的深度对话

文章目录

  • 引言
  • 一、Golang Goroutine
  • 二、C++20 Coroutine
  • 三、Coroutine 媲美Goroutine?
  • 四、Coroutine 的影响力?
  • 五、结语

摘要:C++20 Coroutine 和 Golang Goroutine 是现代编程语言解决并发和异步编程复杂性的重要原语。Goroutine 是 Go 语言的内置特性,易用、运行时调度,简化高并发应用的开发。C++20 Coroutine 是语言层面的低成本抽象,为 C++ 提供构建高性能异步库和框架的强大基石。

引言

基于操作系统线程的并发模型,虽然强大,但有着非常大的资源开销、复杂的同步机制、很难调试的竞态条件,“回调地狱”和上下文切换性能瓶颈。异步编程通过事件循环、回调函数、Future/Promise 等机制,不阻塞主线程的情况处理耗时操作,但代码可读性和维护性就不那么尽如人意了。

因此,轻量级并发原语(协程)逐渐崭露头角,成为优雅方案。用接近同步代码的直观方式编写异步逻辑,同时降低资源消耗和上下文切换的开销。其中,Golang 的 Goroutine 和 C++20 标准引入的 Coroutine 是各自语言生态最具代表性的轻量级并发模型,都承诺能最大简化并发编程,提升应用性能。

虽然目标相似,C++20 Coroutine 和 Golang Goroutine 的设计理念、底层实现、以及对语言生态的影响力上却大相径庭。Goroutine 是 Go 语言的核心特性,开箱即用、由运行时全面管理的并发解决方案;C++ Coroutine 只提供构建极致性能异步框架的底层工具。

这篇文章对这两种截然不同的轻量级并发原语进行深入的技术对比,剖析优势和局限,特别是 C++20 Coroutine 在技术能力上能否跟 Goroutine 匹敌,以及是否能像 Goroutine 那样,对 C++ 社区乃至整个软件行业带来同等量级的范式转变和深远影响力。

一、Golang Goroutine

Golang 是一门为并发而生的语言,并发是一等公民。Go 语言的 Goroutine 是实现并发编程的基础,简化高并发应用的开发。

Go 语言在设计之初就把并发视为核心竞争力:并发即服务。摒弃多线程编程的复杂性,而是利用 Tony Hoare 提出的通信顺序进程 (Communicating Sequential Processes, CSP)模型。这一模型的思想是“不要通过共享内存来通信,而要通过通信来共享内存”。Go 语言的 Goroutine 之间通过Channel(通道)进行数据交换和同步,而不是直接操作共享变量并依赖复杂的锁机制。从根本上避免传统并发编程常见的死锁、竞态条件等问题。

Goroutine 的实现机制:

  • 跟操作系统线程不同,一个 Goroutine 初始时仅占用极小的栈空间(几 KB)。这个栈空间可以根据需要动态增长和收缩,避免不必要的内存浪费,所以 Go 程序可以轻松创建成千上万个 Goroutine。
  • 运行时调度(M:N 模型):Goroutine 不直接映射到操作系统线程。Go 语言有一个内置的运行时调度器,负责把大量的 Goroutine (M) 多路复用到少量的操作系统线程 (N) 上。这个调度器是用户态的,可以在 Goroutine 执行 I/O 操作、等待 Channel 通信或进行函数调用等时机,自动暂停当前 Goroutine 并切换到另一个可运行的 Goroutine,不用操作系统内核的介入。这种 M:N 调度模型很大程度的减少上下文切换的开销,提高 CPU 利用率。
  • 每个 Goroutine 都有自己的独立调用栈。Goroutine 可以在任何函数调用点暂停和恢复,局部变量和执行上下文都保存在自己的栈内。这种“栈式”的特性让 Goroutine 的编程模型非常直观,像编写同步代码一样编写并发逻辑,不用担心状态的保存和恢复。
  • 通信机制:Channel。Channel 是 Goroutine 之间进行安全通信和同步的主要方式。是一种类型安全的管道,可以一个 Goroutine 向其发送数据,另一个 Goroutine 从中接收数据。Channel 可以是带缓冲的或不带缓冲的,提供天然的同步点,确保数据在并发环境有序和正确传递。Go 语言还提供select语句,同时监听多个 Channel 的操作,实现复杂的并发控制逻辑。

Goroutine 的优势:

  • 创建 Goroutine 的语法极其简单,只要在函数调用前加上go关键字。这种直观的语法大大降低并发编程的门槛。
  • 不用关心线程池管理、锁机制、死锁预防等复杂问题,只要关注业务逻辑和数据流。Goroutine 结合 Channel 的模型让并发代码的编写和理解变得更加容易。
  • 内置调度器:Go 运行时自动管理 Goroutine 的生命周期、调度和上下文切换,不用手动编写复杂的调度逻辑。所以, Go 程序能充分利用多核处理器,同时保持良好的可伸缩性。
  • Go 语言的标准库和庞大的第三方库都对 Goroutine 和 Channel 有原生、良好的支持,构建复杂的并发应用轻而易举。

Goroutine 的局限:

  • 运行时依赖:Goroutine 的性能和行为高度依赖 Go 运行时。对运行时开销有极致要求,或者要完全控制底层内存布局和调度策略的场景,Go 语言的抽象层级就不够低。
  • 一定程度的抽象:虽然 Goroutine 提供高效的并发,但毕竟是 Go 语言运行时提供的一种抽象。不能像 C++ 那样,直接操作寄存器、手动管理协程帧,实现极致的底层控制和“零开销”抽象。

Golang 的 Goroutine 擅长场景:

  • 网络服务和微服务:构建高并发的 Web 服务器、API 网关和分布式服务。
  • 高并发后端应用:处理大量并发请求。
  • 数据处理管道:用 Channel 轻松构建数据流处理管道,实现并行数据处理。
  • 批处理任务:把大型任务分解为多个 Goroutine 并行执行,加速处理速度。

二、C++20 Coroutine

C++20 标准引入的 Coroutine (协程) 和 Go 语言把并发作为一等公民的设计理念不同,C++ Coroutine 秉承 C++ 一贯的“零成本抽象”,提供的是一套低层级的语言机制,而不是一个完整的并发解决方案。目标是成为构建高性能异步库和框架的基础。

C++ Coroutine 的设计目标是提供一种机制,能编写看起来像同步代码的异步操作,同时保持 C++ 固有的性能和控制力。

  • Coroutine 机制本身不引入额外的运行时开销,性能跟手动编写的状态机相当。编译器负责把协程转换为高效的代码。
  • C++ Coroutine 不是一个“开箱即用”的并发模型,不包含内置的调度器、I/O 库或通信机制。相反,只提供一套通用的语言特性,在此基础上构建各种复杂的异步抽象。
  • Coroutine 的主要受众是库和框架的作者,可以用这一机制来创建更高效、更易用的异步 API。

C++ Coroutine 的实现机制跟 Goroutine 有很大的不一样:

  • 编译器转换(状态机):编译器遇到一个标记为协程的函数时(也就是包含co_await,co_yield,co_return关键字的函数),会把其转换为一个状态机。这个状态机负责在协程暂停和恢复时保存和恢复其执行上下文。
  • 无栈是 C++ Coroutine 跟 Goroutine 最明显的区别之一。C++ Coroutine 是“无栈”的,协程暂停时,其调用栈会被完全展开,局部变量和状态不会保存在传统的调用栈上。相反,协程的状态(包括局部变量、参数和返回地址)会被编译器打包到一个称为“协程帧”(Coroutine Frame)的结构,这个结构是在堆上分配的。这种设计避免传统线程上下文切换时栈的复制或切换开销,让协程切换极其轻量。
  • 无内置调度器:C++ 标准库只定义协程的语法和底层机制,没有提供任何形式的调度器。协程的暂停和恢复由co_await表达式决定,但何时以及如何在哪个线程上恢复协程的执行,完全取决用户或库提供的执行器。C++ Coroutine 可以跟任何事件循环、线程池或 I/O 框架直接集成。

核心关键字:co_await,co_yield,co_return

  • co_await:暂停当前协程的执行,等待一个“可等待对象”完成,并在可等待对象完成后恢复执行。
  • co_yield:生成一个值并暂停协程,使其成为一个生成器。
  • co_return:从协程返回一个值或表示协程的完成。

C++ Coroutine 的灵活性和可扩展性得益于其底层的Promise 类型和 Awaitable 概念。每个协程都跟一个 Promise 类型关联,该类型定义协程的返回值类型、异常处理方式以及协程的生命周期管理。Awaitable 对象定义await_readyawait_suspendawait_resume三个方法,控制协程何时暂停、如何暂停以及如何恢复。所以,任何人都可以高度定制协程的行为。

C++ Coroutine 的优势:

  • 因为无栈特性和编译器优化的状态机,C++ Coroutine 的上下文切换开销理论上可以接近零,远低于操作系统线程切换,甚至低于 Goroutine 的运行时调度开销。让 C++ Coroutine 在对延迟和吞吐量有极高要求的场景下表现卓越。
  • 可以完全控制协程的生命周期、内存分配(协程帧的分配位置)、以及调度策略。这种底层控制能力是 C++ 语言的精髓,让使得异步代码能以线性的、同步风格的方式书写,彻底告别异步编程常见的“回调地狱”问题。
  • 结合 Promise 类型和 Awaitable 概念,C++ Coroutine 能构建出非常复杂、高效的异步流程、事件驱动架构和状态机。

C++ Coroutine 的缺点:

  • 学习曲线陡峭:相比 Goroutine 的简洁语法,C++ Coroutine 的概念(Promise 类型、Awaitable、协程帧、执行器集成等)相对复杂,要开发者对 C++ 语言特性和异步编程有更深入的理解,学习门槛较高。
  • C++ 标准库不提供开箱即用的调度器和异步 I/O 库。要依赖第三方库或自行实现来管理协程的调度和集成异步 I/O 操作。
  • 虽然很多优秀的库和框架正在积极采用 C++ Coroutine,但生态系统相比 Goroutine 而言还处在发展初期,缺乏统一的、被广泛接受的解决方案,有一定程度的碎片化。

C++ Coroutine 的应用场景:

  • 高性能网络库:构建高效的异步网络服务器、客户端和代理。
  • 游戏引擎:实现复杂的动画序列、AI 行为和游戏逻辑,提高帧率和响应性。
  • 实时系统:对时间敏感的系统用其低延迟特性处理并发事件。
  • 异步 I/O 框架:作为底层构建块,为文件操作、数据库访问提供高效的异步接口。
  • 事件驱动架构:简化复杂事件处理流程的编写。

三、Coroutine 媲美Goroutine?

不同层级的抽象:构建块 vs. 完整解决方案。这是两者最根本的区别:

  • Goroutine:完整的并发解决方案。Goroutine 是 Go 语言运行时的一部分,提供一套从创建、调度到通信的完整、开箱即用的并发模型。只要用go关键字,Go 运行时就会自动管理 Goroutine 的生命周期、调度和跟操作系统线程的映射。是一个高层级的抽象,把并发的复杂性封装在语言和运行时内部。
  • C++ Coroutine:低层级的语言构建块。C++ Coroutine 不是一个完整的并发模型,而是 C++ 语言提供的一组底层机制(co_await,co_yield,co_return、Promise 类型和 Awaitable 概念)。不包含内置的调度器或 I/O 库。C++ Coroutine 的目标是作为构建高性能异步库和框架的基石,要在其之上构建自己的调度器、任务抽象和异步 I/O 接口。是一个低层级的抽象,把控制权最大化交给开发者。

所以,如果是提供一个同样简单易用、开箱即用的并发模型,那么 C++ Coroutine 本身无法直接跟 Goroutine 媲美,因为要额外的库支持。但如果指的是作为构建高性能异步系统的底层能力,那么 C++ Coroutine 有很大的潜力。

性能方面,各有侧重。

C++ Coroutine 的优势:极致性能和精细控制。

  • C++ Coroutine 的无栈特性和编译器生成的状态机,上下文切换开销理论上可以做到极低,接近函数调用,远低操作系统线程切换。
  • 因为没有内置调度器,C++ Coroutine 避免 Go 运行时调度器带来的固定开销。甚至可以完全避免调度器,直接在事件循环恢复协程。
  • 协程帧的分配可以由用户控制,甚至可以实现无堆分配的协程。
  • 适用场景:对延迟、吞吐量和资源利用率有极致要求的场景,如高频交易系统、游戏引擎、实时嵌入式系统、高性能网络库等,C++ Coroutine 能有非常大的优化空间。

Goroutine 的优势:整体效率和易用性。

  • 高效的 M:N 调度:Go 运行时调度器在大多数通用场景下表现都非常出色,能把大量 Goroutine 映射到少量 OS 线程,充分利用多核 CPU。
  • 内置 I/O 多路复用:Go 运行时集成了网络 I/O 的多路复用,让 Goroutine 在等待 I/O 时能自动切换,不用手动管理。
  • 绝大多数高并发应用而言,Goroutine 提供的性能已经绰绰有余,并且在开发效率的加持下,综合效益更高。

从绝对的理论性能和底层控制力来看,C++ Coroutine 有超越 Goroutine 。但在大多数实际应用,Goroutine 提供的“足够好”的性能和极高的开发效率相结合,整体效率上更有竞争力。

开发体验和效率方面,两者差异就非常明显了:

  • Goroutine 学习曲线平缓、开箱即用、快速迭代。
  • C++ Coroutine 学习曲线陡峭。概念复杂、要手动集成、初期开发效率低。

但是,只要 C++ Coroutine 的上层库(std::expectedstd::generator)成熟,使用体验就变得非常简洁。届时,C++ Coroutine 在使用层面的易用性就能跟 Goroutine 媲美,甚至更胜一筹。

生态系统和标准化:

  • Goroutine:统一、成熟的生态。Goroutine 是 Go 语言的核心特性,都用相同的并发模型。而且 Go 标准库的各个模块都原生支持 Goroutine 和 Channel,形成一个统一、强大的生态系统。围绕 Goroutine 和 Channel 形成一套清晰的并发编程范式,社区资源丰富。
  • C++ Coroutine:发展中、碎片化。缺乏标准化调度器/I/O 库,生态碎片化风险。很多优秀的 C++ 库正在积极整合 C++ Coroutine,未来会有在标准上出现。

C++ Coroutine 在技术能力和潜力上足以媲美甚至超越 Goroutine,尤其是要极致性能和底层控制的领域。

但是,在易用性和开箱即用性上,Goroutine 还是有主导优势。提供完整的、容易学习和使用的并发模型,能快速构建高并发应用。

  • Goroutine适合追求开发效率、快速迭代、以及在通用高并发场景下获得“足够好”性能的开发者。
  • C++ Coroutine适合追求极致性能、精细控制、以及作为库和框架作者构建高性能异步基础设施的开发者。

四、Coroutine 的影响力?

Goroutine 的影响力不只是技术上的先进,更在对 Go 语言生态、开发者社区乃至整个行业产生的深远影响:定义 Go 语言的并发范式,降低并发编程的门槛,引发广泛的范式转变。以此为标准,审视 C++ Coroutine 的潜力。

Goroutine 的影响力:

  • Goroutine 几乎是 Go 语言的代名词。和 Channel 结合,共同构成了 Go 语言独有的、简洁而强大的并发模型。任何学习 Go 语言的开发者,都会首先接触并掌握 Goroutine。
  • go func()的简单语法,结合 Go 运行时自动调度和 Channel 的安全通信机制,大量非专业的并发编程开发者也能编写出高效、正确的并发代码。
  • Goroutine 构建高并发、高伸缩性的网络服务非常简单,这跟微服务和云原生架构的需求高度契合,从而在很大程度上推动这些技术栈的普及。
  • Goroutine 的成功,让更多语言和开发者看到轻量级协程的巨大潜力,加速其他语言对类似机制的采纳和发展。

虽然 C++ Coroutine 在技术有强大的潜力,但要带来 Goroutine 那样的广泛影响力,还不够:

  • C++ 是构建块,不是完整解决方案。C++ 语言只是提供低层级的、零成本的抽象,在此基础上构建自己的解决方案。Coroutine 它本身不是一个完整的、开箱即用的并发模型。不提供标准化的调度器、异步 I/O 库或通信机制。要达到 Goroutine 那样的易用性,C++ 要在 Coroutine 之上构建一个或多个事实上的标准库/框架,而这要时间,且不保证能形成统一。
  • 相比go func()的简洁直观,C++ Coroutine 涉及的概念(Promise 类型、Awaitable、协程帧、执行器集成、生命周期管理等)更为复杂和底层。掌握这些概念并要投入相当大的学习成本。
  • 生态系统碎片化。标准库没有提供统一的调度器和异步 I/O 库,C++ Coroutine 的使用依赖各种第三方库。生态系统碎片化,跨项目和跨库的协作不如 Go 那样统一和简单。
  • C++ 已经有std::threadstd::futurestd::async以及各种锁和原子操作等成熟的并发工具。Coroutines 是对现有工具的补充和增强,尤其是在异步 I/O 方面,而不是一个颠覆性的替代品。它更多地是解决特定领域的痛点,而不是改变整个 C++ 的并发编程范式。

虽然很难复制 Goroutine 的影响力模式,C++ Coroutine 还是对 C++ 社区和相关行业产生深远、独特的影响:

  • 提升 C++ 在异步编程领域的竞争力:Coroutine 改善 C++ 编写高性能、高可读性异步代码方面的能力。C++ 在网络服务、游戏引擎、实时系统、高性能计算等对性能和响应性有极致要求的领域,能够更好应对现代异步编程的挑战,保持领先地位。
  • Coroutine 能把复杂的异步流程以同步代码的风格表达,彻底告别 C++ 长期存在的“回调地狱”问题。
  • Coroutine 为库和框架的作者提供强大的底层工具,构建更高效、更易用的异步抽象。未来会看到更多基于 Coroutine 的高性能网络库、任务调度框架和异步 I/O 库涌现,这些库将间接提升整个 C++ 生态的异步编程水平。
  • 推动 C++ 现代化进程。

结论:影响力不同,但同样重要。

C++20 Coroutine 不太可能像 Golang 的 Goroutine 那样,用一种统一、简单的方式,彻底改变 C++ 语言的并发编程范式,并带来同等规模的、对整个行业产生广泛影响力的范式转变。它的影响力更多体现在提升 C++ 在特定高性能异步领域的竞争力,改善现有复杂异步代码的维护性上。

五、结语

Golang 的 Goroutine开箱即用的特性、极简的语法和内置的运行时调度器,成功把并发编程的门槛降至前所未有的低点。

C++20 Coroutine提供的是一套低层级的、极致灵活的语言构建块。不包含内置的调度器或 I/O 库,而是把这些职责交由库作者和开发者自行实现。

关于 C++ Coroutine 能否跟 Goroutine 媲美,以及能否带来同等影响力的问题,结论是:

  • 在技术能力和潜力上,C++ Coroutine 足以媲美甚至超越 Goroutine。特别是极致性能和底层控制的领域,C++ 在异步编程领域有跟 Go 语言一较高下的底层能力。
  • 但是,C++ Coroutine 不太可能带来 Goroutine 那样广泛的、对整个行业产生范式转变的影响力。

随着 C++ Coroutine 生态系统的逐渐成熟,特别是标准化或事实上的标准执行器和异步 I/O 库出现,C++ Coroutine 的易用性会大大提升。届时,C++ 开发者能以更简洁、高效的方式利用这一强大特性。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/26 15:59:17

SAP Gateway OData V4 的 Conditional Handling,ETag 如何守住并发更新与条件读取

在 SAP Fiori 应用里打开一张销售合同,页面从后端读取当前数据。几分钟后,业务人员修改了付款条件并保存。就在这段时间里,另一个会话也可能已经修改了同一张销售合同。如果前端仍然拿着几分钟前的旧数据直接发起 PATCH,而后端毫无判断地接受这次修改,新请求就可能覆盖另一…

作者头像 李华
网站建设 2026/8/26 15:53:37

元初混沌体系 第三卷 卫星互联网全域周天拓扑体系:第三十一篇 手机直连卫星轻量化波束周天分配模型

第三十一篇 手机直连卫星轻量化波束周天分配模型承启前置 方案立论第三十篇已完成低轨星座补星无缝融合、全生命周期无损迭代体系定型,彻底解决巨型星座节点更替、拓扑畸变、迭代残差、业务断连的核心难题,实现低轨接入层拓扑构型、链路稳态、运维迭代的…

作者头像 李华
网站建设 2026/8/26 15:39:51

从counter开始学GPUIX:最小React桌面应用的5个核心概念

从counter开始学GPUIX:最小React桌面应用的5个核心概念 【免费下载链接】gpuix Node.js & React bindings for Zed GPUI. 项目地址: https://gitcode.com/gh_mirrors/gp/gpuix GPUIX 是一个用 React TypeScript 构建原生 GPU 加速桌面应用 的新框架&…

作者头像 李华