目录
一、为什么要学习多线程?
想象一下你在写一个程序:
它要同时下载文件、显示下载进度、还要让用户能随时点击暂停按钮。
如果你用单线程来写,要么下载时界面卡死,要么响应界面时下载停掉。
解决方法就是 —— 多线程。
多线程能让程序同时处理多个任务,让应用更加流畅和高效。
但别急,我们先把几个基础概念理清楚,否则后面会很容易混淆。
二、进程与线程的区别
1. 什么是进程?
进程(Process)就是操作系统为一个正在运行的程序分配的独立资源空间。
每个进程有自己的 内存地址空间(代码段、数据段、堆、栈)。
进程之间一般是隔离的,不能直接访问对方的数据。
创建/销毁进程的开销比较大。
👉 可以把进程类比成“一个独立的公司”,有自己的办公室、员工和资源,外人进不来。
2. 什么是线程?
线程(Thread)是进程里的一个执行单元。
一个进程至少有一个线程(主线程)。
线程之间共享进程的地址空间(所以访问数据更方便)。
创建/切换线程的开销比进程小。
👉 线程就像是“公司里的员工”,大家共用同一个办公室和资料,但可以同时干不同的活。
3. 直观对比
项目 | 进程 | 线程 |
---|---|---|
地址空间 | 独立 | 共享 |
创建/切换开销 | 大 | 小 |
崩溃影响范围 | 一个进程崩溃一般不影响别的进程 | 一个线程崩溃可能拖垮整个进程 |
通信方式 | IPC(管道、消息队列等) | 共享内存,速度快 |
三、并发 vs 并行
这是很多初学者最容易混淆的概念。
并发(Concurrency):逻辑上“同时”执行,但实际上可能是在一颗 CPU 上快速切换。
👉 就像一个人同时看两本书:看一页 A → 翻页 → 看一页 B → 再翻页 → 回来继续 A。并行(Parallelism):物理上真的同时执行,通常是多核 CPU 各干各的。
👉 就像两个人各拿一本书,各自读,不用切换。
📌 总结:
并发 = 时间上交错,提升“响应性”;
并行 = 空间上同时,提升“吞吐量”。
四、线程在程序中怎么工作?
每个线程有自己的 栈(用来保存局部变量、函数调用信息)。
线程共享 堆、全局变量、文件句柄 等资源。
操作系统调度器负责分配 CPU 时间片,决定哪个线程什么时候运行。
👉 换句话说:线程是操作系统调度的最小单位。
五、代码示例:Hello, Thread!
我们用一个最简单的例子来感受一下:
#include <iostream>
#include <thread>
void say_hello(const std::string& name) {
for (int i = 0; i < 5; i++) {
std::cout << "Hello from " << name << " (i=" << i << ")\n";
}
}
int main() {
std::thread t1(say_hello, "thread-1");
std::thread t2(say_hello, "thread-2");
t1.join(); // 等待 t1 完成
t2.join(); // 等待 t2 完成
std::cout << "Main thread finished.\n";
return 0;
}
运行效果
输出可能是这样的(顺序会乱):
Hello from thread-1 (i=0)
Hello from thread-2 (i=0)
Hello from thread-1 (i=1)
Hello from thread-2 (i=1)
...
👉 这里可以看到,两个线程的输出交错在一起,说明它们是并发执行的。
六、常见误区
线程一定会让程序更快? ❌
如果任务是 I/O 密集(比如网络、文件),线程确实能提高效率。
但如果任务是纯计算密集(CPU 满载),线程数超过 CPU 核心数可能会更慢(上下文切换开销大)。
线程和进程谁更安全?
进程更独立,一个崩溃不会影响其他。
线程共享内存,出错时可能直接导致整个进程崩溃。
七、练习题
修改上面的代码,创建 4 个线程,分别打印不同的名字。
在主线程里也调用
say_hello("main")
,看看输出会怎样交错。如果把
t1.join()
改成t1.detach()
,结果会发生什么?
八、总结
进程 = 程序的资源容器;线程 = 执行单元。
并发 ≠ 并行:并发是“切换”,并行是“同时”。
多线程的优势:提升程序响应性和吞吐量。
但多线程也带来新问题:同步、数据竞争、死锁……(后续章节逐步展开)。
👉 下一篇文章:
「线程生命周期全景:创建、运行、终止、回收」
我们将深入讨论线程的出生到消亡的全过程,以及 join
/detach
的区别。