操作系统概念:进程与线程入门
本文最后更新于 22 天前,如有失效请评论区留言。

学操作系统的时候,进程(Process)线程(Thread)绝对是绕不开的核心概念。今天我们来聊聊这俩兄弟到底是什么、有啥区别,以及在 Python 里怎么玩多进程和多线程。

什么是进程?

简单来说,进程是操作系统资源分配的基本单位。当你双击打开一个程序(比如 Chrome),操作系统就会为它创建一个进程,分配独立的内存空间、文件描述符等资源。

每个进程都有自己独立的地址空间,进程 A 的内存和进程 B 的内存是完全隔离的。这也意味着进程之间通信(IPC)需要额外的机制,比如 PipeShared MemoryMessage Queue 等。

你可以把进程想象成一个个独立的工厂,每个工厂有自己的地盘、设备和原材料,互不干扰。

什么是线程?

线程是 CPU 调度的基本单位,一个进程可以包含多个线程。这些线程共享同一个进程的内存空间、文件描述符等资源,但每个线程有自己的栈(Stack)和程序计数器(Program Counter)。

正因为线程共享内存,所以线程间通信比进程间通信要方便得多——直接读写共享变量就行。但也正因为如此,多线程编程更容易出现 竞态条件(Race Condition),需要用锁(Lock)来保护共享资源。

线程就像工厂里的工人,大家共享同一个车间,但每个人有自己的工位(栈)。如果两个人同时抢一台机器,就会出问题——这就是 Race Condition。

进程 vs 线程:关键区别

  • 资源开销:进程开销大(独立地址空间),线程开销小(共享地址空间)
  • 创建速度:进程创建慢,线程创建快
  • 通信方式:进程间需要 IPC 机制,线程间直接读写共享内存
  • 隔离性:进程相互隔离(一个崩溃不影响其他),线程共享内存(一个出问题可能影响整个进程)
  • 调度:线程是 CPU 调度的基本单位,进程是资源分配的基本单位

并发 vs 并行

这两个概念经常被混淆,但其实不一样:

  • Concurrency(并发):多个任务在逻辑上同时执行,可能是交替运行的(单核 CPU 也能并发)
  • Parallelism(并行):多个任务在物理上同时执行,需要多核 CPU

打个比方:一个厨师同时炒菜、煮汤、蒸饭,他交替操作这叫并发;三个厨师各负责一道菜,这叫并行。

Python 中的多线程:threading

Python 通过 threading 模块提供多线程支持。来看个简单例子:

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("所有线程完成")

GIL:Python 多线程的”紧箍咒”

学 Python 多线程,你一定会遇到 GIL(Global Interpreter Lock)。这是 CPython 解释器的一个全局锁,它保证同一时刻只有一个线程在执行 Python 字节码。

这意味着:Python 的多线程不适合 CPU 密集型任务(比如大量计算),因为 GIL 让多线程无法真正并行。但对于 I/O 密集型任务(比如网络请求、文件读写),多线程还是很有效的,因为 I/O 等待时会释放 GIL。

Python 中的多进程:multiprocessing

如果你需要真正的并行计算,就该用 multiprocessing 模块了。它会创建独立的进程,每个进程有自己的 Python 解释器,不受 GIL 限制:

from multiprocessing import Process, Pool
import time

def cpu_heavy(n):
    """CPU 密集型任务"""
    total = 0
    for i in range(n):
        total += i * i
    return total

if __name__ == "__main__":
    # 方式1:手动创建进程
    start = time.time()
    p1 = Process(target=cpu_heavy, args=(5_000_000,))
    p2 = Process(target=cpu_heavy, args=(5_000_000,))
    p1.start(); p2.start()
    p1.join();  p2.join()
    print(f"多进程耗时: {time.time() - start:.2f}s")

    # 方式2:使用进程池(推荐)
    start = time.time()
    with Pool(4) as pool:
        results = pool.map(cpu_heavy, [5_000_000] * 4)
    print(f"进程池耗时: {time.time() - start:.2f}s")

什么时候用多线程,什么时候用多进程?

  • I/O 密集型(网络请求、文件读写、数据库查询)→ 用 threadingasyncio
  • CPU 密集型(数值计算、图像处理、机器学习训练)→ 用 multiprocessing
  • 如果不想手动管理线程/进程,可以考虑 concurrent.futuresThreadPoolExecutorProcessPoolExecutor

线程安全与死锁

多线程编程中最常见的两个坑:

1. Race Condition(竞态条件)

import threading

counter = 0
lock = threading.Lock()

def unsafe_increment():
    global counter
    for _ in range(100000):
        counter += 1  # 不安全!读-改-写不是原子操作

def safe_increment():
    global counter
    for _ in range(100000):
        with lock:       # 用锁保护
            counter += 1

2. Deadlock(死锁)

当两个线程各自持有对方需要的锁,又在等待对方释放,就会陷入死锁。经典场景:

# 危险!可能导致死锁
lock_a = threading.Lock()
lock_b = threading.Lock()

def thread1():
    with lock_a:
        time.sleep(0.1)
        with lock_b:  # 等待 lock_b,但 lock_b 被 thread2 持有
            pass

def thread2():
    with lock_b:
        time.sleep(0.1)
        with lock_a:  # 等待 lock_a,但 lock_a 被 thread1 持有
            pass

避免死锁的方法:按固定顺序获取锁、使用 threading.Lock 的超时机制、或者用 concurrent.futures 等高层抽象。

总结

  • 进程是资源分配单位,线程是 CPU 调度单位
  • 进程隔离但开销大,线程共享但需要同步
  • Python 因为 GIL 的存在,CPU 密集型任务用多进程,I/O 密集型用多线程
  • 多线程编程要注意 Race Condition 和 Deadlock

理解进程和线程是学习操作系统的基础,也是写高性能程序的前提。建议大家动手跑一下上面的代码,感受多线程和多进程的差异。有问题欢迎留言讨论 👋

版权声明:除特殊说明,博客文章均为原创,依据CC BY-SA 4.0许可证进行授权,转载请附上出处链接及本声明。
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇