下载工作台
Python 编程实战

多进程

试读上半部分 · 解锁后可读全文

第 13 章 · 多进程

本章目标:理解进程与线程的区别;使用 multiprocessing 创建子进程与 ProcessPoolExecutor;掌握进程间通信 Queue / Pipe / Manager;能对 CPU 密集任务(批量计算、大 CSV 聚合)做并行加速;知道 if __name__ == "__main__" 在 Windows 上的必要性。

学时建议:4~5 小时(含 2 小时对比实验)

前置ch12 多线程与锁(对比 GIL);ch08 文件读写;ch05 函数。


13.1 进程 vs 线程

对比项线程进程
内存共享同一进程空间独立地址空间
创建开销较小较大
CPython CPU 并行受 GIL 限制真并行
通信共享变量 + 锁Queue / Pipe / 共享内存
典型场景I/O 密集CPU 密集
                    ┌────────────── 主进程 ──────────────┐
                    │  Python 解释器 + 你的代码           │
                    └───┬──────────┬──────────┬──────────┘
                        │          │          │
                   子进程 A    子进程 B    子进程 C   ← 各自 GIL,互不干扰

shop-demo 场景:对 100 万行订单 CSV 做复杂统计、对商品列表批量算折扣哈希——适合进程池。


13.2 创建进程与 Windows 入口保护

Windows spawn 子进程时会重新 import 主模块,必须用:

from multiprocessing import Process
import os


def worker(name: str) -> None:
    print(f"child {name} pid={os.getpid()}")


if __name__ == "__main__":
    p = Process(target=worker, args=("A",))
    p.start()
    p.join()
    print(f"parent pid={os.getpid()}")
要点说明
if __name__ == "__main__"Windows / macOS 默认 spawn 必须
target + args / kwargsThread
join()等待子进程结束

13.3 ProcessPoolExecutor:CPU 并行首选

ch12 ThreadPoolExecutor 同 API,换进程后端:

from concurrent.futures import ProcessPoolExecutor
import math


def is_prime(n: int) -> bool:
    if n < 2:
        return False
    for i in range(2, int(math.sqrt(n)) + 1):
        if n % i == 0:
            return False
    return True


if __name__ == "__main__":
    numbers = range(10_000, 20_000)
    with ProcessPoolExecutor(max_workers=4) as pool:
        primes = list(pool.map(is_prime, numbers))
    print(sum(primes))
参数建议
max_workers通常 = CPU 核数 或 核数 − 1
map保持输入顺序;适合同构任务

对比实验:用 ch12 线程池跑同一 is_prime,记录耗时——线程版几乎无提升。


13.4 进程间通信:Queue

子进程不能安全共享普通 list;用 multiprocessing.Queue

from multiprocessing import Process, Queue


def producer(q: Queue) -> None:
    for i in range(5):
        q.put(f"order-{i}")


def consumer(q: Queue) -> None:
    while True:
        item = q.get()
        if item is None:
            break
        print("got", item)


if __name__ == "__main__":
    q: Queue = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    q.put(None)
    p2.join()

以下内容需解锁后阅读

试读已结束。解锁本章 ¥5.00,或开通年度会员畅读全部教程。
年度会员 ¥199.00/年; 小紫 AI 工作台有效会员 ¥99.00/年

正文仅在服务端鉴权后下发,未付费无法获取下半部分内容。