Skip to main content

异步不是并发

📅 2026-02-05 ✏️ 2026-03-19 CS
No related notes

并发,程序的结构;异步,等待语义,可以实现并发;异步本质是控制流的问题

谁启动并发,谁就必须负责其生命周期

1 · 问题与解决方案

1.1 · 单线程阻塞的困境

在操作系统中,线程是执行任务的最小单位。

一般来说,运行一个用户程序,其就会创建一个线程负责处理任务: 来一个任务,处理一个任务;来了多个任务,则需要排队,一个一个执行; 如果第N个任务因为等待某个 IO 事件(这里指阻塞IO,比如文件可读、可写)而暂停执行了,那么N后面的任务,都需要等着。

1.2 · 传统解决方案

怎么办呢?

  1. 多线程:来一个任务,启动一个线程。好办法,但有局限:线程是比较占用资源的,操作系统不能创建大量线程;
  2. 线程池:既然不能大量线程,那固定一定数量的线程,但是需要排队等待的问题依旧存在;
  3. IO 复用:既然有任务需要等待,那放一起,有一个线程负责轮询看那个任务准备好了,通过回调函数的方式,交由线程池中的线程执行

1.2.1 · IO复用方案的问题#

IO 复用似乎是个很好的办法,但是设想一个任务,其包含数10个等待IO事件的点, 我们就需要拆分这个任务,在每次遇到一个IO等待点的时候,把后续的任务放到回调函数内, 这就增加了代码的复杂度了。

1.3 · async/await方案#

1.3.1 · 核心思想

回到第1点,CPU的核心数量不是固定的嘛?为什么可以来一个任务,就启动一个线程去执行呢? 原因:操作系统有一套调度机制,可以把某些线程给暂停,让其他线程获取CPU核心的使用权,进而执行任务。

那么,我们可不可以在用户程序内实现这套机制:

  1. 创建少量线程,甚至是一个进程,负责执行任务
  2. 当任务需要等待IO事件的时候,将当前任务放置一边(暂停当前任务),执行其他任务
  3. 避免回调函数继续执行任务

1.3.2 · async/await的机制#

为了完成这几点,async/await出来了

  1. 一般来说,一个函数代表一个任务,而用async关键字修饰这个函数,就代表这个任务,是含有IO等待点的
  2. 一般来说,async函数是不可以直接调用的,需要使用await关键字调用函数,表示调用这个函数且在遇到IO等待点时挂起来,去执行其他已经就绪的async函数
  3. 当await调用的函数完成后,会继续往下执行,不需要拆分任务和使用回调函数

2 · async/await实现原理#

2.1 · 概述

lua https://www.lua.org/pil/contents.html#9

https://rainingcomputers.blog/dist/building_a_mental_model_for_async_programs.md

https://maximorlov.com/understanding-async-await/

https://eli.thegreenplace.net/2017/concurrent-servers-part-1-introduction/

https://www.youtube.com/watch?v=Z_OAlIhXziw

2.2 · 核心机制

generators, generator-based coroutines, native coroutines, yield from and await