并发模型完全指南

从高级语言的语法糖,到操作系统内核的调度算法 — 彻底搞懂计算机世界里的"同时做多件事"到底是怎么一回事。

🧵

多线程 / 线程池

操作系统线程是主角。Java ThreadPool、Go goroutine、C++ std::thread 都属于这一类。

Thread-based

异步 / 协程

用户态"轻量级线程",不阻塞 OS 线程。Python asyncio、JavaScript Promise、Rust tokio、C# async/await。

Async/Await
🔄

事件驱动 / Event Loop

单线程 + IO 多路复用。Node.js、nginx、Redis、Vert.x 都靠这个打死高并发。

Event-driven

📖 先搞清楚:并发 vs 并行

这两个词经常被混用,但它们的区别是整个话题的基石。

并发 (Concurrency)

逻辑上同时,物理上可能交替。就像一个人同时处理三件事 — 聊天、写文档、回邮件。每件事切一小段时间轮流做,宏观上感觉像同时在干。单核 CPU 完全可以并发。

并行 (Parallelism)

物理上真正同时执行。需要多个 CPU 核心。就像一个团队三个人分别做三件事,没有任何时间轮转。并行是并发的子集 — 并行一定并发,并发不一定并行。

🎯 三大并发模型速览

模型 调度单位 谁在调度 上下文切换成本 典型代表 适合场景
多线程/线程池 OS 线程 (Thread) 操作系统内核(抢占式调度) 高 (~1-10μs, 涉及内核态切换) Java ThreadPool, C++ std::thread, Go goroutine CPU 密集型计算、需要真正并行的任务
异步/协程 协程 (Coroutine/Fiber) 语言运行时/用户态(协作式调度) 极低 (~几十ns, 纯用户态) Python asyncio, JS async/await, Rust tokio, Kotlin coroutine 高并发 IO(网络请求、数据库查询)
事件驱动 事件 (Event/Callback) Event Loop + IO 多路复用 几乎为零 (单线程,无切换) Node.js, nginx, Redis, libuv, Netty 海量短连接、IO 密集型服务

🧠 核心问题的本质

所有并发模型都在解决同一个问题:当一个线程因为等待 IO 而空闲时,CPU 不该闲着。

假设你有一个 Web 服务器,每个请求需要读数据库(100ms),CPU 处理只要 1ms。如果用传统的"一个线程一个请求"模型,处理 10000 个并发请求就需要 10000 个线程,每个线程 99% 的时间都在等 IO — 大量内存和上下文切换开销被浪费了。

三大模型用不同思路解决这个问题: