异步编程概述

subproblem

编程即是描述解决问题的步骤,其核心在于如何理解“问题”本身。

有时候我们遇到的问题,解决起来是一环扣一环的。如果我想顺利完成我的2024 春夏开源操作系统训练营之旅,就必须先通过第一阶段,再通过第二阶段,最后完成第三阶段的任务。

另外一种情况是,当问题变得复杂时,我们可以尝试把一个大的父问题分解成相互独立的子问题,只要子问题的结果都有了,就可以用这些子问题的解来解决父问题。如果我想要顺利毕业,唯一的要求就是修够学分,我可以选择先修专业选修课1,再修专业选修课2,或者反过来也行,又或者我同时搞定它们。

同步和异步

发现了吗?在上述的第一种情况中,子问题之间相互关联,我们只能按部就班。而在第二种情况下,因为子问题间相互独立,所以我们可以完全不关心以何种顺序去解决它们。

通过这一观察,我们抽象出两类关系。同步指一系列问题或操作之间相互关联,异步指一系列问题或操作之间相互独立。

现在我们可以说上述的第二种情况中,各个子问题之间是异步的,还可以说子问题和父问题之间是同步的。“同步”和“异步”在字面上说明在这里我们关心的是问题的解决步骤。

并行和并发

我们可以完全不关心以何种顺序去解决异步的多个问题,甚至如果我们把这些问题交给不同的人去解决,我们能更快地得到所有子问题的解,进而更快地解决父问题。理想很美好,但停下来想想,就会发现出现了新的问题——我们该怎么知道子问题的解都准备好了?因为把问题交给不同的人去解决,现在我们失去了对这些问题的主导,所以必须增加一个通信的过程来了解问题的解决情况。

作为一个插曲,我们可以抽象出一个新的概念,并行指分配不同的物理资源去解决异步的多个问题。

回到我们的讨论上,将所有的子问题交给其他不同的人去做,那我现在干啥,毕竟还没有所有子问题的解?去打会儿游戏吧,如果闲下来什么都不干会有浪费时间的负罪感哈哈,原谅我不想去帮忙解决这些麻烦的子问题。

好了,现在我们又得到了一个抽象,并发指用同一物理资源去交叉解决多个问题。

并行可以让任务完成得更快,此时我们更关心时间上的效率,但需要投入更多的资源。并发可以更有效地利用物理资源,然而仅仅是并发并不能更快地解决问题。

异步模型

是时候真正讨论异步编程了。我们已经有了足够动机去让一个程序变得异步,更快地解决问题,或是更有效地利用物理资源,或是两者都有。但是我们该怎么做?当真正谈到实现的时候总要考虑更多恼人的细节。

我将向你解释我所理解的三级异步模型:进程(Process)-> 线程(Thread)-> 协程(Coroutine)。注意,这里的 协程 是一个比事实上的协程更广泛的概念,选择它只是为了和 进程线程 对仗。

进程是通用的对完整的运行时程序的抽象,它包括了程序运行所需要的一切,数据、指令、调用栈……

线程是通用的对运行时程序主体部分的抽象,相比于进程,它仅包含程序需要怎么做的部分,即控制流。线程是在进程基础上分离出了控制流,所以现在一个进程可以拥有多个线程和一个所有线程共享的内存空间。

进程和线程是代码层面上通用的抽象,这意味着它们可以作为一个基础提供给所有程序使用,现在几乎所有操作系统都支持进程和线程。然而通用也是有代价的,那就是,为了满足所有程序的需求分配的物理资源,对于某些程序来说就显得多余和浪费了。

协程在代码层面上并不是通用的,所以它不是完全由操作系统提供,而是主要由程序语言提供。由用户代码控制,可以更精确的使用资源,代价是程序本身会变得更庞大。