首页 › programming › 并发不是并行:一堂不过时的神课

并发不是并行:重读 Rob Pike 那堂十年不过时的 Go 神课

概述:在编程世界里,有一道分水岭把程序员分成两类:能把"并发(concurrency)”和“并行(parallelism)”说清楚的人,和说不清楚的人。这道分水岭的标志性界碑,是 Go 语言联合创始人 Rob Pike 在 2012 年 Heroku Waza 大会上的一堂约 30 分钟的演讲——Concurrency Is Not Parallelism。十多年过去,这堂课非但没有过时,反而被后来的结构化并发、Java 虚拟线程、Swift Actor 等技术演进逐一“补票验证”。本文带你完整吸收这堂课的论证结构、理论血脉与争议边界:没看过视频,你能看懂全部核心;看过视频,你会知道它哪里讲对了、哪里被讲歪了。

一、视频基本信息

项目 内容
标题(原文) Concurrency Is Not Parallelism
标题(中译) 并发不是并行
演讲者 Rob Pike(贝尔实验室出身,Unix / Plan 9 / UTF-8 核心成员,Go 语言联合创造者)
频道 Heroku 官方 YouTube 频道(另有多个会议方与社区转载版本)
发布时间 2012 年 Heroku Waza 开发者大会现场演讲录像(精确上线日期据频道页面)
时长 约 30 分钟
热度 常青经典:十多年来长期出现在 Hacker News、Reddit 等社区的“并发入门第一课”推荐清单中,每逢多核与异步话题的讨论都会被重新引用(精确播放数本次未能实时抓取,以频道页面为准)
配套幻灯片 Go 官网托管:go.dev/talks/2012/waza.slide

注:本文写作时未能实时抓取频道页面,凡无法核实的数据一律从略标注,未作任何精确数字的凭空标注。

二、核心主张拆解:一堂课的五个论证步骤

1. 先把两个词钉死:结构 vs 执行

整堂课最著名的一句话,出现在演讲开篇的幻灯片上:

"Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once." (并发是同时应对多件事,并行是同时做成多件事。)

Pike 的定义非常克制:并发是一种程序结构方式——把程序设计成多个可以独立推进的执行体,彼此通过消息协作;并行是一种执行方式——多个计算真的在同一个物理瞬间发生。关键推论是:两者是正交的。你可以写出结构上高度并发、但运行在单核上靠时间片轮转的程序(没有并行);也可以有完全不做并发设计的并行(比如 GPU 上的数据并行、SIMD 指令)。

这句话之所以是分水岭,是因为它把一个物理问题(有没有多个核)和一个设计问题(程序怎么组织)拆开了。大多数“并发恐惧症”,源于把这两件事捆绑在一起想。

2. 硬件背景:为什么 2012 年人人都该听这堂课

演讲的时点选得极准。2005 年前后,处理器主频的红利基本见顶:功耗墙挡住了继续提频的路(芯片行业称之为“登纳德缩放定律”的失效),厂商集体转向“堆核数”的路线。同年,C++ 委员会元老 Herb Sutter 发表了那篇后来被引用了无数次的文章——《The Free Lunch Is Over》(免费午餐结束了),警告软件行业:过去几十年“什么都不做、程序自动变快”的时代终结了,以后想快,你必须写出能利用多核的程序。

Pike 在演讲中把这个背景一笔带过,却点出了更冷静的一层:多核不是学习并发的唯一理由。这直接引出第三步。

3. 最反直觉的主张:并发结构的价值,独立于并行

这是整堂课的灵魂,也是被最多听众忽略的部分。Pike 明确说:并发设计的目标首先是让程序更易懂、更易组织,并行只是良好并发结构带来的“可能的副产品”。

他的论据是:很多程序天然就是“同时应对多件事”的——服务器同时面对多个连接、编辑器同时响应输入和后台拼写检查、运行时同时做垃圾回收和业务计算。如果用传统单线程顺序模型去写,你得手写状态机、手动交错各种任务;而如果把每个独立任务写成一个并发的执行单元,程序结构就和问题结构对齐了——哪怕最后只跑在一个核上。演讲里他还专门展示了并发垃圾回收、图形界面响应这类例子:它们的收益是响应性和清晰度,不是吞吐量。

4. 用什么结构承载并发:从 Dijkstra 到 Go 的血脉

并发结构不止一种,Pike 给出的答案是 Go 的 CSP 路线:goroutine(轻量执行体)+ channel(类型化管道)+ select(多路选择)。他给出那句后来被写进 Go 官方格言的话(同样见于 Effective Go):

Don't communicate by sharing memory; share memory by communicating. (不要靠共享内存来通信,要靠通信来共享内存。)

翻译成工程语言:与其让多个线程围绕同一块可变内存加锁互搏,不如让数据“所有权”随消息在执行体之间流动——同一时刻只有一个执行体碰这块数据,锁就失去了存在的大半理由。

5. 演示与模式:地鼠、传送带与负载均衡

这堂课最出圈的,是那组地鼠(gopher)动画:要把一堆书搬走,你可以拆成“装车、运输、卸货”三段独立任务,用传送带(channel)连接;吞吐不够,就加地鼠(worker)、加传送带——任务分解、消息传递、扇入扇出、负载均衡,并发设计的全部基本动作,在动画里被压缩成几分钟的直觉。Pike 现场用 channel 手写了负载均衡器的骨架代码,演示了这套路数如何让“横向扩展”从一个分布式系统的宏大命题,退化为局部几行代码的组合。

三、知识与理论溯源:这堂课不是 Pike 发明的

理解这堂课的深层价值,必须看到它背后的半个多世纪的学术谱系——这也是它区别于“经验分享”的地方:

四、批判性审查:哪些站得住,哪些要打折扣

站得住的部分。第一,概念区分本身早已是教科书共识(如 CS:APP、《Operating Systems: Three Easy Pieces》的并发章节都用类似框架),Pike 的贡献是用“结构 vs 执行”四个字把它变得可传授。第二,预言被后续十年验证:Trio 库提出的结构化并发(Martin Sústrik 2016 年提出概念、Nathaniel Smith 2018 年的长文使其流行)、Swift 5.5(2021)引入 async/await 与 Actor、Java 经 Project Loom 在 JDK 21(2023)交付虚拟线程、Kotlin 协程 2018 年稳定化——整个行业不约而同走向“轻量执行体 + 显式取消/作用域”,方向与这堂课一致。第三,工业战绩扎实:Docker、Kubernetes、etcd、Terraform 等基础设施全部构建在这套模型上。

需要打折扣的部分。其一,“并发让程序更易懂”是一个有前提的设计主张,不是定律。channel 并不自动消灭竞态——所有权纪律靠程序员自觉,编译器不强制;Go 官方在 2013 年前后就不得不内置竞态检测器(基于 ThreadSanitizer),恰恰说明“靠通信共享内存”仍会写出数据竞争。死锁和 goroutine 泄漏(忘记关闭的 channel 挂住执行体)是这套模型的专属新坑。其二,性能上存在被回避的代价:消息传递有序列化/调度开销,在超低延迟交易系统、DPDK 网络栈这类领域,无锁共享内存依然是王座。其三,加速比问题被轻描淡写:Amdahl 定律(1967)提醒你程序里串行部分是天花板;Gustafson(1988)虽然从“扩大问题规模”的视角给出更乐观的修正,但对“加了 goroutine 就快了”的民间误读,两条定律都是一盆冷水。其四,Go 自身花了八年才补齐模型的短板——直到 Go 1.14(2020)才有异步抢占,此前一个不含调度点的死循环 goroutine 可以饿死整个 P——说明“轻量执行体”的优雅之下是实实在在的运行时工程债务。

一个公允的结论:这堂课的立论在“概念辨析 + 结构主张”层面近乎无懈可击;它的乐观,只在其把工程代价说得过于轻巧时才需要警惕。

五、认知提升与实用启示

启示一:用“单核检验法”审视你的设计。写下任何并发代码前,先问一句:“如果只有一个核,这个结构还成立吗?”如果答案为否,说明你在用并发技巧掩盖设计问题;如果答案为是——任务边界清晰、数据所有权明确——那么并行收益只是时间问题。并发先服务可读性,再服务性能。

启示二:把四个最小模式练进肌肉记忆。管道(pipeline)、扇入/扇出、工作池(worker pool)、退出信号(done/cancel)。每个都能在二十行内手写实现,是这堂课动画背后的全部操作集。配上一条铁律:测试永远带 -race 跑,取消信号永远显式传递(Go 里即 context),不给“泄漏的 goroutine”留出生土壤。

启示三:跨模型对照,破除“物理必然”错觉。用 Elixir/Erlang 的邮箱模型、Rust 的所有权与异步运行时、或 Java 虚拟线程,重解同一个练习题(比如并发爬虫)。你会发现“并发长什么样”是语言的设计选择而非物理必然——这一步能把你从“某个框架的用户”提升为“并发模型的选择者”,这正是 Pike 这堂课真正想教的东西。

六、视频论证结构图

flowchart TD
    subgraph HW["硬件背景"]
        A["主频红利见顶<br/>登纳德缩放失效(2005年前后)"] --> B["多核与大规模并行硬件普及"]
    end
    subgraph CORE["Pike 的核心论证"]
        B --> C["并发 = 结构<br/>同时应对多件事"]
        B --> D["并行 = 执行<br/>同时做成多件事"]
        C --> E["两者正交:<br/>并发是设计,并行是执行"]
        D --> E
        E --> F["并发结构的价值独立于并行:<br/>清晰、响应性、与问题结构对齐"]
    end
    subgraph LINE["理论血脉"]
        G["Dijkstra 信号量(1965)"] --> H["Hoare CSP 论文(1978)"]
        H --> I["Occam / Transputer(1980年代)"]
        I --> J["Newsqueak / Alef / Limbo(贝尔实验室)"]
        J --> K["Go:goroutine + channel + select(2009)"]
    end
    F --> L["实践模式:<br/>管道 / 扇入扇出 / 负载均衡 / 退出信号"]
    K --> L
    L --> M["后续验证:结构化并发、Swift Actor、<br/>Kotlin 协程、Java 虚拟线程(2023)"]
    L --> N["代价与争议:<br/>竞态仍需检测器、死锁/泄漏新坑、消息传递开销"]

七、延伸阅读

  1. C.A.R. Hoare《Communicating Sequential Processes》(CACM 1978)——这堂课全部思想的源头文本,配合 Wikipedia 的 CSP 词条入门更平滑。
  2. Nathaniel Smith《Notes on structured concurrency, or: go statement considered harmful》(2018)——十年后对这堂课最精彩的一次“补丁与修正”。

参考来源

  1. Rob Pike:Concurrency Is Not Parallelism 演讲幻灯片(Go 官网托管) — 演讲原文与核心定义的权威版本;视频本体可在 YouTube 站内检索标题观看(Heroku 官方频道及会议方上传版本)。
  2. Herb Sutter:The Free Lunch Is Over — 2005 年多核转折点的标志性文章。
  3. Nathaniel Smith:Notes on structured concurrency — 结构化并发对 Go 式并发的反思与修正。
  4. The Go Memory Model(官方文档) — 官方对竞态、同步与 channel 语义的严格定义,是“channel 不自动免疫竞态”的直接证据。

本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵爱卫生(微信ID: gprshome201101)维护,聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入