序 1
前言 3
第1 章 Rust 并发基础 9
1.1 Rust 中的线程 .10
1.2 作用域线程 14
1.3 共享所有权和引用计数 .17
1.3.1 静态对象 .17
1.3.2 泄漏 17
1.3.3 引用计数 .18
1.4 借用和数据竞争 21
1.5 内部可变性 23
1.5.1 Cell .24
1.5.2 RefCell .25
1.5.3 Mutex 和RwLock 26
1.5.4 原子类型 .26
1.5.5 UnsafeCell 27
1.6 线程安全性:Send 和Sync .27
1.7 锁:互斥锁和读写锁 30
1.7.1 Rust 的互斥锁 30
1.7.2 锁中毒 33
1.7.3 读写锁 35
1.8 等待:线程停放与条件变量 37
1.8.1 线程停放 .37
1.8.2 条件变量 .40
1.9 总结 .42
第2 章 原子性 45
2.1 原子加载和存储操作 46
2.1.1 示例:停止标志 47
2.1.2 示例:进度报告 48
2.1.3 示例:延迟初始化 .50
2.2 获取—修改操作 52
2.2.1 示例:多线程进度报告 53
2.2.2 示例:统计 55
2.2.3 示例:ID 分配 .57
2.3 比较—交换操作 59
2.3.1 示例:无溢出的ID 分配 .61
2.3.2 示例:延迟一次性初始化 62
2.4 总结 .64
第3 章 内存顺序 67
3.1 重排与优化 67
3.2 内存模型69
3.3 happens-before 关系 70
3.4 宽松顺序73
3.5 释放和获取顺序 76
3.5.1 例子:锁定 80
3.5.2 示例:通过间接方式实现延迟初始化 .82
3.6 消费顺序85
3.7 顺序一致性顺序 87
3.8 栅栏 .88
3.9 常见误解93
3.10 总结 95
第4 章 构建我们自己的自旋锁 97
4.1 最简实现98
4.2 不安全的自旋锁 .100
4.3 使用锁卫士的安全接口 103
4.4 总结 106
第5 章 构建我们自己的通道 109
5.1 一个基于互斥锁的简单通道 .109
5.2 一个不安全的一次性通道 . 111
5.3 通过运行时检查提高安全性 . 115
5.4 通过类型系统提高安全性 .120
5.5 通过借用避免分配 .125
5.6 阻塞 129
5.7 总结 132
第6 章 构建我们自己的Arc 135
6.1 基础引用计数 135
6.1.1 测测它 140
6.1.2 修改 .141
6.2 弱指针 .143
6.3 优化 150
6.4 总结 158
第7 章 理解处理器 161
7.1 处理器指令 162
7.1.1 加载和存储 .166
7.1.2 Read-Modify-Write 操作 168
7.1.3 Load-Linked 指令和Store-Conditional 指令 172
7.2 缓存 177
7.2.1 缓存一致性 .178
7.2.2 对性能的影响 .180
7.3 重排 186
7.4 内存顺序.188
7.4.1 x86-64:强内存序 189
7.4.2 ARM64:弱内存序 .191
7.4.3 一个实验 194
7.4.4 内存屏障 196
7.5 总结 198
第8 章 操作系统原语 201
8.1 与内核交互 201
8.2 POSIX .203
8.3 Linux 207
8.3.1 Futex 208
8.3.2 Futex 操作 211
8.3.3 优先级继承Futex 操作 .215
8.4 macOS .216
8.5 Windows .217
8.5.1 重量级内核对象 218
8.5.2 轻量级对象 .218
8.5.3 基于地址的等待 220
8.6 总结 221
第9 章 构建我们自己的锁 223
9.1 Mutex 225
9.1.1 避免系统调用 .229
9.1.2 进一步优化 .231
9.1.3 基准测试 234
9.2 条件变量.237
9.2.1 避免系统调用 .243
9.2.2 避免错误唤醒 .245
9.3 读写锁 .248
9.3.1 避免忙循环的写入者252
9.3.2 避免写入者饥饿 255
9.4 总结 258
第10 章 想法和灵感 . 261
10.1 信号量 261
10.2 RCU 262
10.3 无锁链表 264
10.4 队列锁 265
10.5 停车位锁 266
10.6 序列锁 267
10.7 教学材料 268