并发编程是计算机科学中的一个重要领域,尤其在多核 CPU 广泛使用、任务响应要求更高的今天,越来越成为开发者必须掌握的能力。
一、并发编程的历史来源
背景演进:
-
单核时代(1950s-1980s):
- 初期计算机处理速度慢,CPU资源非常宝贵。
- 为了提高CPU利用率,引入了多任务调度(如批处理系统、时分多道程序设计)。
-
多进程/线程操作系统(1980s-1990s):
- Unix 等操作系统引入进程并发概念。
- 进程之间彼此独立,通过进程间通信(IPC)协作。
- 为提高效率,引入线程:轻量级的执行单元,多个线程共享进程内存空间。
-
多核处理器普及(2000s-至今):
- 多核 CPU 使得真正的并行计算成为可能。
- 并发编程从“模拟并行”发展为“真实并行”。
- 同时,对并发的正确性、安全性、性能要求更高,推动了线程池、协程、并发框架(如 Java 的 Fork/Join、Go 的 goroutine)等的发展。
二、并发编程的本质需求:它要解决什么问题?
本质需求:
并发编程的目标不是让程序“跑得更快”,而是解决多个任务在有限资源下如何协同执行的问题:
| 问题 | 并发编程解决方案 |
|---|---|
| 单线程不能同时处理多个任务(如IO+计算) | 协同调度,异步编程 |
| 多任务访问共享资源容易冲突 | 加锁、原子变量、CAS 等 |
| 多核 CPU 利用率低 | 线程、进程并行计算 |
| 响应速度慢(如GUI卡顿、后端响应慢) | 异步执行、任务分发 |
| 程序中存在等待操作(如网络、磁盘) | 非阻塞IO、事件驱动 |
三、什么场景下需要用到并发编程?
常见应用场景:
| 场景 | 描述 |
|---|---|
| ✅ Web服务器 | 高并发用户请求,需要线程/协程池处理 |
| ✅ GUI 程序 | 主线程负责界面,后台线程做耗时操作 |
| ✅ 数据处理任务 | 大量数据可并行处理(MapReduce、ETL) |
| ✅ 游戏引擎 | 渲染、物理计算、音效需要并行协作 |
| ✅ 网络编程 | 网络收发、协议解析并发进行(NIO、epoll) |
| ✅ 高性能计算(HPC) | 并行计算模型(多线程、多进程、GPU) |
四、并发编程的基础概念有哪些?
核心概念体系:
1. 基础模型:
- 进程(Process):操作系统分配资源的最小单位
- 线程(Thread):CPU 调度执行的最小单位,多个线程共享进程内资源
- 协程(Coroutine):用户空间调度的轻量级线程(如 Python asyncio,Go 协程)
2. 并发模型:
- 多线程 vs 多进程
- 事件驱动模型(Reactor、Proactor)
- 异步编程(Async/Await、回调)
3. 状态与问题:
- 线程安全(Thread Safety)
- 死锁(Deadlock)、活锁(Livelock)
- 饥饿(Starvation)、竞态(Race Condition)
4. 同步与通信机制:
- 锁机制:互斥锁(Mutex)、读写锁、递归锁、信号量(Semaphore)
- 原子操作:CAS(Compare-And-Swap)
- 条件变量、Barrier、CountDownLatch、CyclicBarrier 等
- 线程间通信(管道、消息队列、共享内存)
5. 并发框架/技术栈(按语言):
| 语言 | 并发机制 |
|---|---|
| Java | Thread、ExecutorService、synchronized、Lock、Fork/Join |
| C++ | std::thread、std::mutex、std::future |
| Go | goroutine、channel |
| Python | threading、multiprocessing、asyncio |
| JavaScript | 事件循环 + 异步 Promise |
总结:
并发编程是为了解决多个任务共享资源、合理调度、提高响应与吞吐的一种程序设计方法,贯穿于操作系统、网络编程、大数据计算等各个领域。
