|
| 1 | +Title: KAIST CS492C - 并发编程速读(Part1. 介绍) |
| 2 | +Date: 2025-10-06 10:00 |
| 3 | +Tags: 并行计算, 并发, Rust, 系统编程 |
| 4 | +Slug: kaist-cs492c-1 |
| 5 | + |
| 6 | +<div class="alert alert-warning" role="alert"> |
| 7 | + ⚠️ 本文根据视频字幕和 slides 由 AI 生成 |
| 8 | +</div> |
| 9 | + |
| 10 | +[课程主页](https://github.com/kaist-cp/cs431) |
| 11 | + |
| 12 | +## 并发编程导论:走进平行计算的新时代 |
| 13 | + |
| 14 | +在 21 世纪的科技浪潮中,人工智能与物联网正推动着计算需求的爆炸式增长。自从 2016 年 AlphaGo 战胜围棋大师李世乭的那一刻起,人类正式步入了“并行计算的时代”——一个由成千上万计算单元协同运作、驱动智能算法与复杂系统的新时代。 |
| 15 | + |
| 16 | +## 为什么我们需要并行? |
| 17 | + |
| 18 | +随着 AI 与大数据的兴起,单核 CPU 的性能提升已逐渐触顶。计算机架构的历史告诉我们——两次重大的技术转折推动了“并行化”的浪潮: |
| 19 | + |
| 20 | +1. **Dennard Scaling 的终结(约 2005 年)** |
| 21 | + 过去,我们可以通过提高 CPU 主频来提升性能。但功耗与发热限制了频率的进一步增长。于是,多核系统(multi-core)成为新的出路——不再让单个核心更快,而是让更多核心同时工作。 |
| 22 | + |
| 23 | +2. **摩尔定律的放缓(约 2018 年)** |
| 24 | + 晶体管密度的增长速度逐渐减缓,性能提升进入瓶颈。于是出现了新的突破口——**加速器(Accelerators)**。GPU、TPU、NPU、FPGA 等专用硬件通过极致的并行化,实现了远超传统 CPU 的吞吐能力。 |
| 25 | + |
| 26 | +如今,从智能手机到超级计算机,计算资源不仅“更多”,而且“更异构”。我们拥有多核 CPU、GPU、TPU、分布式服务器……但问题也随之而来——**如何协调这些并行资源,让它们真正协同而不是竞争?** |
| 27 | + |
| 28 | +## 并行与并发:两个不同的概念 |
| 29 | + |
| 30 | +在进入技术细节之前,我们需要区分两个常被混淆的词: |
| 31 | + |
| 32 | +* **Parallelism(并行)**:多个任务在不同资源上同时执行。 |
| 33 | + |
| 34 | + > 例如,多个 CPU 核心同时计算不同的任务。 |
| 35 | +
|
| 36 | +* **Concurrency(并发)**:多个任务共享可变资源(shared mutable state)。 |
| 37 | + |
| 38 | + > 例如,多个线程同时访问同一个数据库或内存。 |
| 39 | +
|
| 40 | +并行让我们“同时做多件事”,而并发让我们“安全地共享”。 |
| 41 | +真正高性能的系统,往往需要两者兼备。 |
| 42 | + |
| 43 | +## 并发的挑战:非确定性(Nondeterminism) |
| 44 | + |
| 45 | +并发之所以难,不是因为代码复杂,而是因为它**不可预测**。 |
| 46 | + |
| 47 | +1. **指令交错(Interleaving)** |
| 48 | + 当多个线程同时修改同一变量时,执行顺序的不确定性会导致不同结果。 |
| 49 | + 例如: |
| 50 | + |
| 51 | + ```text |
| 52 | + Thread 1: X = 1 |
| 53 | + Thread 2: X = 2 |
| 54 | + ``` |
| 55 | + |
| 56 | + 程序的最终值取决于哪个线程“先执行”,这在多核环境中是不可控的。 |
| 57 | + |
| 58 | +2. **硬件与编译器重排序(Reordering)** |
| 59 | + 即使你以为代码按顺序执行,CPU 和编译器可能会为了优化性能而重新排列指令。 |
| 60 | + 在现代体系结构下,甚至可能出现两个线程都读到旧值的情况(如 a=b=0)。 |
| 61 | + |
| 62 | +非确定性带来了“组合爆炸式”的复杂度,也让调试并发程序成为噩梦。 |
| 63 | + |
| 64 | +## 驯服混乱:两种应对策略 |
| 65 | + |
| 66 | +为了让并发变得“可控”,研究者与工程师提出了两大方向: |
| 67 | + |
| 68 | +1. **通过安全的 API 封装复杂性** |
| 69 | + 例如锁(lock)、条件变量(condition variable)或线程库等,都在提供一种“安全抽象”: |
| 70 | + |
| 71 | + * 我们不需要了解底层实现; |
| 72 | + * 只需遵循 API 规范,就能安全地同步并发操作。 |
| 73 | + 举例来说,锁的核心保证是:“任意时刻,最多只有一个线程持有它。” |
| 74 | + |
| 75 | +2. **通过成熟的同步模式实现安全性** |
| 76 | + 对于实现者而言,底层需要使用经过验证的**同步原语**(如 release-acquire 模型、原子操作 CAS 等),以确保正确性与性能平衡。 |
| 77 | + 换言之:**上层开发者依赖安全 API,下层开发者掌握同步模式。** |
| 78 | + |
| 79 | +## 并发编程的核心目标 |
| 80 | + |
| 81 | +并发编程的终极目标不是“让所有线程都跑起来”,而是**在安全性、效率和可扩展性之间取得平衡**。 |
| 82 | +正如课程中所强调的: |
| 83 | + |
| 84 | +> “Too much nondeterminism leads to scalability problems, |
| 85 | +> too little leads to correctness problems.” |
| 86 | +
|
| 87 | +我们要学会“驯服刚刚好的非确定性”——既不过分限制并行性,也不让错误无处可查。 |
| 88 | + |
| 89 | +--- |
| 90 | + |
| 91 | +### 总结:从混乱到秩序的艺术 |
| 92 | + |
| 93 | +《Concurrent Programming》课程从这节导论开始,为我们揭示了一个真相: |
| 94 | +**并发不是附加在程序上的技巧,而是现代计算的本质。** |
| 95 | + |
| 96 | +从 AlphaGo 背后的千万线程,到你电脑中悄然运行的多任务系统, |
| 97 | +并发编程是一场让“混乱的世界”高效、有序运转的艺术。 |
| 98 | + |
| 99 | +接下来的课程将深入探讨: |
| 100 | + |
| 101 | +* 如何安全地同步多线程操作; |
| 102 | +* 锁与无锁编程的权衡; |
| 103 | +* Rust 语言中的所有权模型; |
| 104 | +* 以及如何在理论与实践中构建真正高效的并发系统。 |
0 commit comments