第 14 课导言

介绍并发编程的基本概念:局部状态、时间以及并发计算的复杂性。

导言

本课中,我们将讨论并发(concurrency)的基础知识。

先修内容

你应当理解赋值(assignment)和可变数据。

阅读材料

本课的大部分内容基于这些讲义和 SICP 3.4 节。

并发导言

在第 3 单元中,我们看到了带有局部状态(local state)的计算对象作为建模工具的威力。但这种威力是有代价的。

引入赋值,就迫使我们把时间(time)纳入计算模型。在引入赋值之前,我们所有的程序都是无时间的,即任何有值的表达式总是有同一个值。相比之下,回忆一下 SICP 3.1.1 节开头介绍的模拟银行账户取款并返回结果余额的例子:

> (withdraw 25)
75
> (withdraw 25)
50

这里,同一表达式的连续求值产生不同的值。这种行为源于这样一个事实:赋值语句的执行(本例中是对变量 balance 的赋值)划分了值发生变化的时刻。求值一个表达式的结果不仅取决于表达式本身,还取决于求值发生在这些时刻之前还是之后。用带局部状态的计算对象来建模,迫使我们直面时间,把它当作编程中的一个本质概念。

我们可以进一步组织计算模型,使之与我们对物理世界的感知相匹配。世界上的对象不是一次一个地顺序变化。相反,我们感知到它们是并发(concurrently)行动的——同时发生。因此,把系统建模成并发执行的计算过程(process)的集合往往很自然。正如我们可以通过用具有独立局部状态的对象组织模型来使程序模块化,把计算模型划分为单独并发演化的部分往往也是恰当的。

除了使程序更模块化,并发计算还能比顺序计算提供速度优势。顺序计算机一次只执行一个操作,所以完成一项任务所需的时间与执行的操作总数成正比。然而,如果能把问题分解成相对独立、只需很少通信的若干部分,就有可能把各部分分配给独立的计算处理器,产生与可用处理器数量成正比的速度优势。

不幸的是,赋值带来的复杂性在并发存在的情况下变得更加棘手。并发执行的事实——无论是因为世界本身并行运行,还是因为我们的计算机如此——给我们对时间的理解带来了额外的复杂性。