1. 多线程基础:进程与线程的区别、并发与并行的概念、线程的生命周期、Python/C++中的线程模型对比

1.1 进程与线程:别再傻傻分不清

说实话,我刚开始学多线程那会儿,也搞混过进程和线程。后来在做一个高并发服务器项目时,才真正体会到它们的区别。

进程,你可以把它想象成一个独立的程序实例。每个进程都有自己的内存空间、文件描述符、环境变量。说白了,进程之间是隔离的,一个进程挂了,不影响另一个。

线程呢?它是进程内部的执行单元。同一个进程里的线程共享内存空间,可以互相访问数据。嗯,这里要注意——共享内存既是优势也是麻烦的来源。

核心区别一句话总结:

  • 进程是资源分配的最小单位
  • 线程是CPU调度的最小单位
  • 进程间通信需要IPC(管道、消息队列、共享内存等)
  • 线程间通信直接读写共享变量就行

我在项目中遇到过一个问题:用多进程处理大量并发连接,结果内存爆了。每个进程都要加载一份完整的运行时库,开销太大。后来改成多线程,内存占用直接降了60%。

1.2 并发与并行:你以为的并行可能只是并发

这两个概念,我面试过不少人,十个有八个说不清楚。

并发:多个任务在同一个时间段内交替执行。单核CPU也能做到,靠的是时间片轮转。你感觉它们在同时跑,其实是在快速切换。

并行:多个任务在同一个时刻真正同时执行。这需要多核CPU的支持。每个核心跑一个任务,互不干扰。

举个例子:你一边写代码一边听音乐,这叫并发(你的大脑在快速切换)。你左手画圆右手画方,这叫并行(两个手同时动)。

我的经验:写代码时别盲目追求并行。并发模型往往更容易调试和维护。我见过太多人一上来就搞并行,结果死锁、竞态条件一堆,最后改回并发反而跑得更稳。

1.3 线程的生命周期:从生到死

线程不是凭空出现的,它有完整的生命周期。我习惯把它分成五个阶段:

  1. 新建(New):线程对象创建了,但还没启动。就像你招了个员工,还没让他干活。
  2. 就绪(Runnable):调用了start(),线程等待CPU分配时间片。员工已经站在工位前,等电脑开机。
  3. 运行(Running):CPU开始执行线程的代码。员工正在干活。
  4. 阻塞(Blocked/Waiting):线程因为等待资源(锁、I/O、sleep)而暂停。员工等打印机、等审批。
  5. 终止(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 本章知识体系

下面这张图是我自己画的,把本章的核心逻辑串起来了。你看一眼就能明白进程、线程、并发、并行之间的关系。

多线程基础核心知识体系 进程 vs 线程 进程:资源分配最小单位 线程:CPU调度最小单位 进程间通信:IPC(管道、共享内存) 线程间通信:共享变量 进程开销大,线程开销小 并发 vs 并行 并发:交替执行(单核也能实现) 并行:同时执行(需要多核CPU) 并发靠时间片轮转 并行靠多核硬件 并发易调试,并行性能高 线程生命周期 新建(New) → 就绪(Runnable) 就绪 → 运行(Running) 运行 → 阻塞(Blocked) 阻塞 → 就绪 → 运行 运行 → 终止(Terminated) Python vs C++ 线程模型 Python:有GIL,适合I/O密集型 C++:无GIL,真正并行 Python:threading.Thread C++:std::thread 选对语言,事半功倍

这张图把四个核心知识点串在了一起。左边是进程/线程和生命周期,右边是并发/并行和语言对比。你对照着看,思路会清晰很多。

本章核心要点:

  • 进程是资源容器,线程是执行单元
  • 并发是逻辑上的同时,并行是物理上的同时
  • 线程有五个状态:新建、就绪、运行、阻塞、终止
  • Python的GIL限制了CPU密集型任务的并行能力
  • C++的std::thread提供真正的并行执行

好了,这一章就到这里。记住这些基础概念,后面讲锁、条件变量、线程池的时候,你会觉得轻松很多。

可可精品资料库