Demystifying C++20 Coroutines
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