前言
大家好,我是 倔强青铜三 。微信公众号:倔强青铜三
,点赞、收藏、关注,一键三连!
今天是 苦练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 |
感谢阅读!欢迎关注微信公众号:
倔强青铜三
。点赞、收藏、关注,一键三连!