上篇介绍了 C++20 协程的诸多内容,独余 co_await 未曾涉及,它是协程中非常重要的一个关键字,用以支持挂起(suspend)和恢复(resume)的逻辑。 本篇便专门来对其进行介绍。 Awaitable type and Awaiter type 较于普通函数,协程支持挂起和恢复。那么何时挂起,何时恢复,便是逻辑之所在。 由于许多问题在解决时都无法立刻得到答案,即结果存在延迟性,在程序中就表现为阻塞。阻塞会导致CPU大量空闲,效率大减,于是就要想办法实现非阻塞。 多线程便是解决阻塞的一个方式,遇到阻塞,便由操作系统进行切换调度,以此实现非阻塞。重叠 IO 亦是一个非阻塞方案,遇到阻塞,提供一个回调函数给操作系统,系统在阻塞完成后调用其继续执行。 这些方案,本质上都是在处理如何挂起和恢复的问题。换言之,就是在遇到阻塞时暂停当前工作,先去进行别的工作,等阻塞完成后再回来继续完成当前工作。 既然拥有共同的处理问题的逻辑,那么对其进行抽象,便能得到一个高层级的类型。这个高层级的类型便是 Awaitable type(即上篇的 Awaitable object)。 简单地说,Awaitable type 就是对阻塞问题进行总结、归纳、提炼要点,所得到的模型。 那么有何好处呢? 好处就是,我们只要依照抽象后所得模型中的一些规则,便能定义出所有类似问题的解决逻辑,所有类似问题都能依此模型进行解决。 乍听很复杂,其实并不难,只要依规则行事便可。 那么具体规则又是什么? 其实只是三个接口: await_ready() await_suspend() await_resume() 它们分别代表着:是否阻塞、挂起、恢复。 将程序中阻塞完成的条件,写到 await_ready 函数中,便能依此决策何时挂起,何时恢复。 一个类型若直接实现了这三个接口,那么这个类就被称为 Awaiter。 什么意思呢?若类型 A 本身并未实现这三个接口,而是通过类型 B 实现的,那么类型 A 就称作 Awaitable,类型 B 称作 Awaiter。… Continue Reading C++20 Coroutines: operator co_await

Introduction 自 C++11 开始,很多更新都集中在并发支持上,从最初的线程基础支持,到如今的协程,C++ 已经日趋完善。 协程是继线程之后的又一利器,若论年龄,协程倒比线程要大。由于早期的线程主要在单 CPU 上运行,仅是模拟多线程,故又称“伪线程”。伪线程和协程的思路颇有相似之处,以致难以旌别,其实它们各有应用场景。 本篇将回答下列问题: 什么是协程? 协程与进程和线程有何区别? 什么是并发,和并行有何区别? 协程和函数有何区别? 什么是有栈协程,无栈协程? 协程的使用场景有哪些? Coroutines TS 是什么? 如何使用 C++20 Coroutines? 本篇旨在明析概念,不会出现太多代码,概念清晰后,下篇将有大量代码来解析协程。 Concurrency versus Parallelism 要理清协程的定位,须得先能够旌别并发与并行。可以先来看看百科的定义: 并行是指“并排行走”或“同时实行或实施”。 在操作系统中是指,一组程序按独立异步的速度执行,无论从微观还是宏观,程序都是一起执行的。 对比地,并发是指:在同一个时间段内,两个或多个程序执行,有时间上的重叠(宏观上是同时,微观上仍是顺序执行)。 这里用了两个词:宏观和微观。宏观指从大的角度来看,微观指从小的角度来说。通俗地说,宏观指的是能够通过眼睛看到的,微观则是眼睛看不到的。并发在宏观上同时,微观上为顺序执行。这就是伪线程的实现思路,通过迅速地在各个模块之间切换执行,以迷惑视觉,使得看起来像是同时执行的,其实底层依旧是顺序执行的。 线程在早期并非必须,因为都是命令窗口,就是从上往下依次执行的。而 Windows 拥有界面,在执行任务之时可以随意进行别的操作,因此界面常常会卡死。MS 便搞了个模拟同步运行的机制,便是线程。了解过 CPU 发展历史的会知道,早期 CPU 频率步步直升,人们都以为这种趋势能够持续下去。这也意味着你写的程序不用更新也能随着 CPU 的增强而自动增强性能。 所以早期都是单核 CPU。随着物理瓶颈到来,频率提升之路步步维艰,生产厂商便转变策略,通过将多个核心置于一起,产生多核CPU来提升性能。这便是性能不够,数量来凑。也因此,真正的多线程得以实现。 真线程的执行便为并行执行。由于每个核心都是独立的,因此可以同时执行。此时,无论是视觉上,还是底层实现上,都是真正的同时执行。 既然有并发与并行两种形式,那么我们对其进行排列组合,便能得出4种结果。 一个既无并行,也无并发的程序处于并发的能力最弱,只能顺序执行每条任务,这一般是一个很小的程序。当有并发,无并行时,能够充分发挥单核CPU的性能,伪线程与协程便属此列。而无并发,有并行时,也就是说是多核CPU,但一个核心上只开了一个线程,此时的确是并发。但和有并发无并行时所达到的效果是一样的,真线程便属此列。 也正因如此,线程和协程的效果只用眼睛是无法分辨的。有很多人便说有了多线程为啥还要加入协程呢?其实协程和线程分工并不同,一个是并发,一个是并行,不过伪线程也属于并发罢了。 那现在你可能又要改变问题了,既然伪线程也是并行,那么请问协程和伪线程又如何区分? 这个问题才是关键,其实主要区别是调度问题,本文第四节将会详细对比协程与线程。 只有并发,或只有并行,都无法处理高并发需求,所以二者呈互补之势,共同发挥CPU的最大性能。 下面再以一个例子来谈谈并发与并行。… Continue Reading Demystifying C++20 Coroutines