问:进程与线程之间有什么区别?C++ 项目中应该怎么选?
答
一句话概括:进程是资源分配和隔离的基本单位,线程是 CPU 调度和执行的基本单位。 一个进程至少包含一个线程,同一进程中的线程共享进程资源,但各自拥有独立的栈和寄存器上下文。
| 对比项 | 进程 | 线程 |
|---|---|---|
| 地址空间 | 通常拥有独立的虚拟地址空间 | 共享所属进程的地址空间 |
| 资源 | 独立管理文件描述符、堆、全局数据等资源 | 共享进程资源,拥有自己的栈、寄存器和线程局部存储 |
| 创建与切换 | 创建、切换和销毁的成本通常更高 | 成本通常更低,但仍可能产生调度和缓存开销 |
| 通信 | 需要管道、Socket、共享内存等进程间通信机制 | 可以直接读写共享内存,但必须使用锁、原子操作等同步手段 |
| 隔离性 | 一个进程崩溃通常不会直接破坏另一个进程的地址空间 | 一个线程越界或未定义行为可能拖垮整个进程 |
| 典型用途 | 服务隔离、权限隔离、故障域隔离 | 并行计算、异步任务、共享状态的并发处理 |
为什么线程共享资源仍然需要同步?
共享地址空间只解决了“能不能看见同一份数据”,没有解决“多个执行流同时修改数据是否正确”。例如下面的自增操作不是一个不可分割的动作:
int counter = 0;
// 两个线程同时执行 counter++ 可能发生数据竞争
在 C++ 中,未同步的并发读写会产生数据竞争,结果属于未定义行为。可以使用 std::mutex 保护临界区,或者在适合的场景使用 std::atomic<int>:
#include <atomic>
#include <thread>
std::atomic<int> counter{0};
void add_one()
{
++counter;
}
int main()
{
std::thread first(add_one);
std::thread second(add_one);
first.join();
second.join();
}
选择建议
- 需要强隔离、独立升级或一个组件崩溃不能影响主服务时,优先考虑多进程。
- 需要共享内存、降低通信成本或并行执行任务时,可以使用多线程,但要先设计好所有权、同步和退出策略。
- 多线程不是“免费并行”:线程数过多会增加上下文切换、锁竞争和缓存失效。I/O 密集型任务与 CPU 密集型任务也应分别评估。
因此,面试回答不能只说“线程比进程轻量”。更完整的答案应该同时说明:线程共享资源所以通信方便,也因为共享资源而更容易产生数据竞争;进程隔离更强,但通信和创建的成本通常更高。