苦练Python第65天:CPU密集型任务救星!多进程multiprocessing模块实战解析,攻破GIL限制!

前言

大家好,我是 倔强青铜三 。微信公众号:倔强青铜三,点赞、收藏、关注,一键三连!

今天是 苦练Python第65天 ,咱们把"并行"这门重炮------multiprocessing 模块,从开箱到实战一次性讲透。


一、为什么需要进程?

  • CPU 密集 场景:图像处理、矩阵计算、加密解密,GIL 让线程干瞪眼。
  • 真·并行:利用多核,进程彼此独立,无 GIL 约束。
  • 隔离内存:进程崩溃不拖主程序下水,比线程更安全。

提示:I/O 密集请继续用 threading;CPU 密集才上 multiprocessing


二、30 秒启动你的第一个子进程

python 复制代码
# demo_hello_process.py
from multiprocessing import Process
import os
import time

def say_hello(name, delay):
    time.sleep(delay)
    print(f"你好,{name},来自进程 {os.getpid()}")

if __name__ == '__main__':
    p = Process(target=say_hello, args=("倔强青铜三", 2))
    p.start()
    p.join()
    print("主进程结束")

运行效果:

复制代码
你好,倔强青铜三,来自进程 12345
主进程结束

三、批量任务:手写进程池

场景:并发计算 4 个平方数。

python 复制代码
# demo_pool.py
from multiprocessing import Pool
import os
import time

def square(x):
    print(f"进程 {os.getpid()} 正在处理 {x}")
    time.sleep(1)
    return x * x

if __name__ == '__main__':
    nums = [1, 2, 3, 4]
    with Pool(processes=4) as pool:
        res = pool.map(square, nums)
    print("结果:", res)

运行效果:

css 复制代码
进程 11111 正在处理 1
进程 22222 正在处理 2
进程 33333 正在处理 3
进程 44444 正在处理 4
结果: [1, 4, 9, 16]

四、进程安全:Lock 互斥锁

共享计数器,不加锁会算错。

python 复制代码
# demo_lock.py
from multiprocessing import Process, Lock, Value
import time

counter = Value('i', 0)  # 共享整数
lock = Lock()

def add_one(n):
    global counter
    for _ in range(n):
        with lock:
            counter.value += 1

if __name__ == '__main__':
    processes = [Process(target=add_one, args=(100000,)) for _ in range(4)]
    for p in processes:
        p.start()
    for p in processes:
        p.join()
    print("最终计数:", counter.value)

运行效果:

复制代码
最终计数: 400000

五、进程通信:Queue 队列

生产者-消费者模型,无需锁。

python 复制代码
# demo_queue.py
from multiprocessing import Process, Queue
import time

def producer(q):
    for i in range(3):
        q.put(i)
        print(f"生产 {i}")
        time.sleep(1)

def consumer(q):
    while True:
        item = q.get()
        if item is None:  # 毒丸
            break
        print(f"消费 {item}")

if __name__ == '__main__':
    q = Queue()
    p1 = Process(target=producer, args=(q,))
    p2 = Process(target=consumer, args=(q,))
    p1.start()
    p2.start()
    p1.join()
    q.put(None)  # 结束信号
    p2.join()

运行效果:

复制代码
生产 0
消费 0
生产 1
消费 1
生产 2
消费 2

六、共享内存:Value & Array

无需复制,直接共享。

css 复制代码
# demo_shared_memory.py
from multiprocessing import Process, Value, Array

def modify(v, a):
    v.value = 42
    for i in range(len(a)):
        a[i] = -a[i]

if __name__ == '__main__':
    v = Value('d', 3.14)
    a = Array('i', range(4))
    p = Process(target=modify, args=(v, a))
    p.start()
    p.join()
    print("Value:", v.value)
    print("Array:", a[:])

运行效果:

makefile 复制代码
Value: 42.0
Array: [0, -1, -2, -3]

七、进程事件:Event 同步

父进程通知子进程起跑。

python 复制代码
# demo_event.py
from multiprocessing import Process, Event
import time

def runner(e, name):
    print(f"{name} 就位")
    e.wait()
    print(f"{name} 起跑!")

if __name__ == '__main__':
    e = Event()
    for i in range(3):
        Process(target=runner, args=(e, f"选手{i+1}")).start()
    time.sleep(2)
    print("裁判:预备------跑!")
    e.set()

运行效果:

复制代码
选手1 就位
选手2 就位
选手3 就位
裁判:预备------跑!
选手1 起跑!
选手2 起跑!
选手3 起跑!

八、Manager 代理对象

共享高级 Python 对象(列表、字典等)。

scss 复制代码
# demo_manager.py
from multiprocessing import Process, Manager
import os

def worker(d, l, idx):
    d[os.getpid()] = idx
    l.append(idx)

if __name__ == '__main__':
    with Manager() as manager:
        d = manager.dict()
        l = manager.list()
        jobs = [Process(target=worker, args=(d, l, i)) for i in range(5)]
        for j in jobs:
            j.start()
        for j in jobs:
            j.join()
        print("共享 dict:", dict(d))
        print("共享 list:", list(l))

运行效果:

yaml 复制代码
共享 dict: {11111: 0, 22222: 1, 33333: 2, 44444: 3, 55555: 4}
共享 list: [0, 1, 2, 3, 4]

九、完整实战:多进程文件哈希计算器

功能:并发计算多个文件 MD5,统计总耗时。

python 复制代码
# demo_hash.py
import os
import hashlib
import time
from multiprocessing import Pool, cpu_count

def file_md5(path):
    h = hashlib.md5()
    try:
        with open(path, 'rb') as f:
            while chunk := f.read(1 << 16):
                h.update(chunk)
        print(f"{os.path.basename(path)} 完成")
        return path, h.hexdigest()
    except FileNotFoundError:
        return path, None

if __name__ == '__main__':
    files = [__file__] * 6  # 用本文件复制 6 次测试
    start = time.time()
    with Pool(processes=cpu_count()) as pool:
        results = pool.map(file_md5, files)
    for path, md5 in results:
        print(f"{os.path.basename(path)} -> {md5}")
    print(f"总耗时 {time.time() - start:.2f}s")

运行效果:

rust 复制代码
demo_hash.py 完成
demo_hash.py 完成
demo_hash.py 完成
demo_hash.py 完成
demo_hash.py 完成
demo_hash.py 完成
demo_hash.py -> 5d41402abc4b2a76b9719d911017c592
...
总耗时 0.12s

十、with 上下文管理器 × multiprocessing:优雅又安全

Pool、Lock、Manager 都支持 with,自动清理,拒绝僵尸。

python 复制代码
# demo_with.py
from multiprocessing import Pool

def f(x):
    return x * x

if __name__ == '__main__':
    with Pool() as pool:
        print(pool.map(f, [1, 2, 3]))
    # 离开 with 后子进程全部 join,无需手动

运行效果:

csharp 复制代码
[1, 4, 9]

小结

武器 用途 备注
Process 创建子进程 记得 if __name__ == '__main__'
Pool 进程池 首选 map/imap,自动管理
Queue 进程通信 线程安全,无需锁
Value/Array 共享内存 快但只支持简单类型
Manager 共享复杂对象 慢一些,功能全
Lock/Event 同步原语 支持 with

感谢阅读!欢迎关注微信公众号:倔强青铜三

点赞、收藏、关注,一键三连!

相关推荐
Panda__Panda2 小时前
docker项目打包演示项目(数字排序服务)
运维·javascript·python·docker·容器·c#
强哥之神2 小时前
浅谈目前主流的LLM软件技术栈:Kubernetes + Ray + PyTorch + vLLM 的协同架构
人工智能·语言模型·自然语言处理·transformer·openai·ray
zskj_qcxjqr2 小时前
七彩喜艾灸机器人:当千年中医智慧遇上现代科技
大数据·人工智能·科技·机器人
怪兽20143 小时前
SQL优化手段有哪些
java·数据库·面试
Lris-KK3 小时前
力扣Hot100--94.二叉树的中序遍历、144.二叉树的前序遍历、145.二叉树的后序遍历
python·算法·leetcode
Zack_Liu3 小时前
深度学习基础模块
人工智能·深度学习
zy_destiny4 小时前
【工业场景】用YOLOv8实现抽烟识别
人工智能·python·算法·yolo·机器学习·计算机视觉·目标跟踪
狠活科技4 小时前
免登录!免安装ClI,Claude Code官方插件接入API使用教程
人工智能·vscode·ai编程
(●—●)橘子……4 小时前
记力扣2009:使数组连续的最少操作数 练习理解
数据结构·python·算法·leetcode