1. 多线程基础:进程与线程的区别、并发与并行的概念、线程的生命周期、Python/C++中的线程模型对比
1.1 进程与线程:别再傻傻分不清
说实话,我刚开始学多线程那会儿,也搞混过进程和线程。后来在做一个高并发服务器项目时,才真正体会到它们的区别。
进程,你可以把它想象成一个独立的程序实例。每个进程都有自己的内存空间、文件描述符、环境变量。说白了,进程之间是隔离的,一个进程挂了,不影响另一个。
线程呢?它是进程内部的执行单元。同一个进程里的线程共享内存空间,可以互相访问数据。嗯,这里要注意——共享内存既是优势也是麻烦的来源。
核心区别一句话总结:
- 进程是资源分配的最小单位
- 线程是CPU调度的最小单位
- 进程间通信需要IPC(管道、消息队列、共享内存等)
- 线程间通信直接读写共享变量就行
我在项目中遇到过一个问题:用多进程处理大量并发连接,结果内存爆了。每个进程都要加载一份完整的运行时库,开销太大。后来改成多线程,内存占用直接降了60%。
1.2 并发与并行:你以为的并行可能只是并发
这两个概念,我面试过不少人,十个有八个说不清楚。
并发:多个任务在同一个时间段内交替执行。单核CPU也能做到,靠的是时间片轮转。你感觉它们在同时跑,其实是在快速切换。
并行:多个任务在同一个时刻真正同时执行。这需要多核CPU的支持。每个核心跑一个任务,互不干扰。
举个例子:你一边写代码一边听音乐,这叫并发(你的大脑在快速切换)。你左手画圆右手画方,这叫并行(两个手同时动)。
我的经验:写代码时别盲目追求并行。并发模型往往更容易调试和维护。我见过太多人一上来就搞并行,结果死锁、竞态条件一堆,最后改回并发反而跑得更稳。
1.3 线程的生命周期:从生到死
线程不是凭空出现的,它有完整的生命周期。我习惯把它分成五个阶段:
- 新建(New):线程对象创建了,但还没启动。就像你招了个员工,还没让他干活。
- 就绪(Runnable):调用了start(),线程等待CPU分配时间片。员工已经站在工位前,等电脑开机。
- 运行(Running):CPU开始执行线程的代码。员工正在干活。
- 阻塞(Blocked/Waiting):线程因为等待资源(锁、I/O、sleep)而暂停。员工等打印机、等审批。
- 终止(Terminated):线程执行完run()或发生异常。员工下班了。
避坑指南:我曾经在项目里犯过一个低级错误——在线程还没启动时就调用了join()。结果主线程直接卡死。记住:只有处于Runnable状态的线程才能被join。
1.4 Python vs C++:线程模型大不同
这两种语言我都用了十几年,它们的线程模型差异很大。你想想看,Python的GIL(全局解释器锁)让多线程在CPU密集型任务上几乎没用,而C++的多线程才是真正的并行。
| 特性 | Python | C++ |
|---|---|---|
| 线程创建 | threading.Thread(target=func) | std::thread t(func) |
| 锁机制 | threading.Lock | std::mutex |
| GIL影响 | 有,CPU密集型受限 | 无,真正并行 |
| 内存模型 | 自动管理 | 手动管理,需注意数据竞争 |
| 适用场景 | I/O密集型、网络请求 | CPU密集型、高性能计算 |
Python示例:
import threading
import time
def worker(name):
print(f"线程 {name} 开始工作")
time.sleep(2)
print(f"线程 {name} 结束工作")
# 创建线程
t1 = threading.Thread(target=worker, args=("A",))
t2 = threading.Thread(target=worker, args=("B",))
# 启动线程
t1.start()
t2.start()
# 等待线程结束
t1.join()
t2.join()
print("所有线程执行完毕")
C++示例:
#include <iostream>
#include <thread>
#include <chrono>
void worker(const std::string& name) {
std::cout << "线程 " << name << " 开始工作" << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(2));
std::cout << "线程 " << name << " 结束工作" << std::endl;
}
int main() {
// 创建并启动线程
std::thread t1(worker, "A");
std::thread t2(worker, "B");
// 等待线程结束
t1.join();
t2.join();
std::cout << "所有线程执行完毕" << std::endl;
return 0;
}
我的建议:如果你做Python多线程,遇到CPU密集型任务,别死磕threading。用multiprocessing模块或者直接上C++扩展。我在一个图像处理项目里,把Python的线程池换成C++的std::async,性能提升了8倍。
1.5 本章知识体系
下面这张图是我自己画的,把本章的核心逻辑串起来了。你看一眼就能明白进程、线程、并发、并行之间的关系。
这张图把四个核心知识点串在了一起。左边是进程/线程和生命周期,右边是并发/并行和语言对比。你对照着看,思路会清晰很多。
本章核心要点:
- 进程是资源容器,线程是执行单元
- 并发是逻辑上的同时,并行是物理上的同时
- 线程有五个状态:新建、就绪、运行、阻塞、终止
- Python的GIL限制了CPU密集型任务的并行能力
- C++的std::thread提供真正的并行执行
好了,这一章就到这里。记住这些基础概念,后面讲锁、条件变量、线程池的时候,你会觉得轻松很多。
可可精品资料库