并发编程基础:线程、进程、并发与并行
各位同学,咱们正式开始第二讲。在做国密SDK优化之前,有些基础概念得先掰扯清楚。别小看这些基础,很多并发瓶颈追根溯源,就是对这几个概念理解不透。
1. 进程与线程:到底有啥区别?
我记得刚入行那会儿,有同事问我“进程和线程啥区别”,我背了一堆定义,结果人家越听越糊涂。后来我用一个例子讲明白了:进程就像一家饭店,线程就是饭店里的服务员。饭店有自己的厨房、库房、大堂(内存空间),服务员共享这些资源,但每个服务员有自己的活儿要干。
核心差异一览表
| 特性 | 进程 | 线程 |
|---|---|---|
| 资源拥有 | 独立地址空间、文件句柄等 | 共享所在进程的资源 |
| 创建开销 | 大(fork需要复制页表等) | 小(仅分配栈和寄存器) |
| 通信方式 | 需IPC(管道、共享内存、Socket) | 直接读写共享变量 |
| 隔离性 | 高,一个进程挂了一般不影响别的 | 低,一个线程写越界可能整个进程崩 |
| 切换代价 | 高,需切换页表、刷新TLB | 低,仅切换寄存器和栈指针 |
我在项目中遇到过一个问题:有个国密签名服务,用多进程做负载隔离。结果业务高峰期时,进程创建太频繁,CPU全耗在fork和上下文切换上了。后来改成线程池,延迟降了一半。你想想看,进程切换确实太“重”了。
我个人的经验:在国密SDK这种高性能场景下,尽量用线程而不是进程。除非你需要强隔离(比如多租户场景),否则线程的开销优势很明显。
2. 并发与并行:别傻傻分不清
这两个词很多人混着用,其实完全是两码事。打个比方:你一边吃面一边刷手机——这叫并发(看起来同时做,其实是快速切换)。你左手写代码右手敲键盘——这叫并行(真的同时做)。
- 并发(Concurrency):多个任务在同一个CPU核上交替执行,通过时间片轮转实现"同时"推进。
- 并行(Parallelism):多个任务在不同CPU核上真正同时执行。
为什么必须分清?因为国密SDK的优化方向截然不同。如果瓶颈在单核并发,你要优化锁竞争、减少上下文切换;如果瓶颈在多核并行,你要考虑数据分片、NUMA亲和性。
我曾经踩过的坑:早期优化SM4加解密时,我盲目开了大量线程以为就能跑满多核。结果锁争用导致大量线程在等锁,CPU利用率上不去。后来才发现,并发不等于并行——你的任务如果串行依赖太多,再多核也是白搭。
这里给个直观对比表,你品品:
| 场景 | 实现方式 | CPU核心数 | 吞吐量观察 |
|---|---|---|---|
| 单核并发 | 时间片轮转 | 1 | 总吞吐 ≤ 1核极限 |
| 多核并行 | 任务分区到各核 | ≥2 | 理想倍速 = 核数 |
| 超线程并发 | 一个核偷空跑两线程 | 1(物理) | 提升约20%~30% |
3. Amdahl定律:你的优化天花板在哪?
说白了,这个定律告诉你:即使你把并行的部分优化到飞起,整个系统的加速比也有上限——被串行部分卡死了。
公式很简单:
加速比 = 1 / ((1 - P) + P / N)
P = 可并行比例
N = CPU核心数
举个例子:假设你的国密验签任务,90%可以并行,剩下10%必须串行(比如最后的验签结果校验)。
- 4核:加速比 = 1 / (0.1 + 0.9/4) ≈ 3.08
- 16核:加速比 = 1 / (0.1 + 0.9/16) ≈ 6.40
- ∞核:加速比 = 1 / 0.1 = 10(天花板!)
看到了吧?哪怕你有再多的核,串行那10%决定了最大只能加速10倍。这就是天花板。
实际案例:我在优化一个国密证书链验证SDK时,发现串行部分占到了30%(包括证书链校验、CRL检查等)。当时技术人员一窝蜂堆线程,结果加速比从没超过3.3。后来我们重构了串行流程,把能并行的部分拆出来(比如各证书签名验证并行),串行比例降到8%,加速比直接翻倍。
避坑指南:做并发优化前,先用性能分析工具(比如perf、vtune)量一下你的串行比例。如果串行超过20%,先优化串行逻辑,再堆核心。否则你就是在给跑车装火箭发动机,但前轮还是自行车轮胎。
下面是这一章的知识体系框架,我手绘了一张图,你看一眼就能串起来:
这张图把四个概念串起来了。进程和线程是资源/执行的关系,并发和并行是逻辑/物理的关系,Amdahl定律则是评估并行收益的标尺。做国密SDK优化时,你脑子里得时刻挂着这张图——你现在在调哪一块?是减少线程切换(进程vs线程),还是提高并行度(并发vs并行),还是测串行比例(Amdahl)?
小结一下:今天我讲了三个基础但极其重要的概念。我个人习惯在开始任何并发优化前,先画一张类似上面的图,把当前系统的“进程/线程模型”、“并发/并行策略”、“串行比例”标出来。光这一张图,就能帮你避免一半的踩坑。
最后提醒:别觉得基础简单就跳过。我见过太多人,一上来就调线程数、改锁策略,结果问题出在进程设计不合理或者串行比例没算过。万丈高楼平地起,基础打牢了,后面咱们聊国密SDK的具体瓶颈时,你才跟得上。