文章目录
- 前言
- [一、 进程与线程的概念](#一、 进程与线程的概念)
- [二、 为什么需要多进程?](#二、 为什么需要多进程?)
- [三、 Process 类:创建和管理进程](#三、 Process 类:创建和管理进程)
-
- [3.1 创建单个进程](#3.1 创建单个进程)
- [3.2 创建多个进程](#3.2 创建多个进程)
- [3.3 Process 类的重要属性和方法](#3.3 Process 类的重要属性和方法)
- [四、 进程池(Pool):高效管理多个进程](#四、 进程池(Pool):高效管理多个进程)
-
- [4.1 使用进程池](#4.1 使用进程池)
- [4.2 进程池的高级用法](#4.2 进程池的高级用法)
- [五、 进程间通信(IPC)](#五、 进程间通信(IPC))
-
- [5.1 管道(Pipe)](#5.1 管道(Pipe))
- [5.2 队列(Queue)](#5.2 队列(Queue))
- [六、 进程间同步](#六、 进程间同步)
- 七、进程间共享状态
-
- [7.1 共享内存(Value和Array)](#7.1 共享内存(Value和Array))
- [7.2 服务进程(Manager)](#7.2 服务进程(Manager))
- [八、 实际应用示例](#八、 实际应用示例)
-
- [8.1 并行数据处理](#8.1 并行数据处理)
- [九、 注意事项和最佳实践](#九、 注意事项和最佳实践)
-
- [9.1 重要注意事项](#9.1 重要注意事项)
- [9.2 最佳实践总结](#9.2 最佳实践总结)
- 总结
前言
本文主要介绍进程与线程的概念、Process类、进程池、进程间通信以及应用示例等知识点。
一、 进程与线程的概念
进程:一个正在运行的应用程序实例,拥有独立的内存空间和系统资源。例如:启动一个音乐播放器应用程序就是一个进程。
线程:进程内部的最小执行单元,一个进程可以包含多个线程,它们共享进程的内存空间。例如:在音乐播放器中选择并播放一首歌曲就是一个线程任务。
二、 为什么需要多进程?
在多线程编程中我们了解到,由于 GIL(全局解释器锁) 的存在,CPython 解释器下的多线程在 CPU 密集型任务中无法实现真正的并行计算。为了充分利用多核 CPU 的优势,Python 提供了 multiprocessing 模块。
multiprocessing 模块的优势:
使用子进程代替线程,绕过 GIL 限制
每个进程有独立的 Python 解释器和内存空间
可实现真正的并行计算
API 与 threading 模块类似,学习成本低
三、 Process 类:创建和管理进程
3.1 创建单个进程
python
python
from multiprocessing import Process
import os
import time
def task(name, duration):
"""子进程要执行的任务"""
print(f'子进程 {name} 开始 (PID: {os.getpid()})')
time.sleep(duration)
print(f'子进程 {name} 结束 (PID: {os.getpid()})')
if __name__ == '__main__':
# 主进程信息
print(f'主进程 ID: {os.getpid()}')
# 创建进程对象
p = Process(target=task, args=('任务1', 2))
# 启动进程
p.start()
print('主进程继续执行其他工作...')
# 等待子进程结束
p.join()
print('子进程已结束,主进程继续')
3.2 创建多个进程
python
python
from multiprocessing import Process
import os
import time
def worker(task_id, sleep_time):
print(f'工作进程 {task_id} (PID: {os.getpid()}) 开始')
time.sleep(sleep_time)
print(f'工作进程 {task_id} (PID: {os.getpid()}) 完成')
return f'任务{task_id}_结果'
if __name__ == '__main__':
processes = []
# 创建多个进程
for i in range(5):
p = Process(target=worker, args=(i, i+1))
processes.append(p)
p.start()
print(f'已启动进程 {i}')
# 等待所有进程完成
for i, p in enumerate(processes):
p.join()
print(f'进程 {i} 已完成等待')
print('所有进程执行完毕')
3.3 Process 类的重要属性和方法
python
python
from multiprocessing import Process
import time
import os
def long_running_task():
print(f'进程 {os.getpid()} 开始运行')
time.sleep(5)
print(f'进程 {os.getpid()} 运行结束')
if __name__ == '__main__':
# 创建进程
p = Process(target=long_running_task, name='工作进程')
# 进程属性
print(f'进程名称: {p.name}')
print(f'是否守护进程: {p.daemon}')
# 启动前
print(f'启动前是否存活: {p.is_alive()}')
# 启动进程
p.start()
print(f'启动后是否存活: {p.is_alive()}')
print(f'进程ID: {p.pid}')
# 等待一段时间后检查
time.sleep(1)
print(f'1秒后是否存活: {p.is_alive()}')
# 设置超时等待
p.join(timeout=2)
print(f'等待2秒后是否存活: {p.is_alive()}')
# 强制终止进程(谨慎使用)
if p.is_alive():
p.terminate()
print('进程已被终止')
# 获取退出代码
print(f'退出代码: {p.exitcode}')
# 清理资源
p.close()
四、 进程池(Pool):高效管理多个进程
4.1 使用进程池
python
python
from multiprocessing import Pool
import time
import os
def compute_square(n):
"""CPU密集型任务示例"""
print(f'进程 {os.getpid()} 计算 {n} 的平方')
time.sleep(1) # 模拟耗时计算
return n * n
if __name__ == '__main__':
# 创建进程池(默认使用所有可用的CPU核心)
with Pool(processes=4) as pool:
# 同步执行(阻塞方式)
print('--- 同步执行 ---')
result = pool.apply(compute_square, (5,))
print(f'同步结果: {result}')
# 异步执行(非阻塞方式)
print('\n--- 异步执行 ---')
async_results = []
for i in range(10):
# 提交任务到进程池
result = pool.apply_async(compute_square, (i,))
async_results.append(result)
# 获取异步执行结果
for i, result in enumerate(async_results):
print(f'任务{i}结果: {result.get(timeout=5)}') # 设置超时时间
print('所有任务完成,进程池已自动关闭')
4.2 进程池的高级用法
python
python
from multiprocessing import Pool
import time
def process_data(data_chunk):
"""处理数据块的函数"""
time.sleep(0.5) # 模拟处理时间
processed = [x * 2 for x in data_chunk]
return processed, sum(processed)
def init_worker():
"""工作进程初始化函数"""
print(f'工作进程初始化完成')
def process_callback(result):
"""回调函数,在主进程中执行"""
processed_data, total = result
print(f'处理完成,总和: {total}')
def error_callback(error):
"""错误回调函数"""
print(f'处理出错: {error}')
if __name__ == '__main__':
# 准备数据
data = [list(range(i, i+5)) for i in range(0, 50, 5)]
# 创建进程池,设置初始化函数和最大任务数
with Pool(
processes=3,
initializer=init_worker,
maxtasksperchild=3 # 每个子进程最多执行3个任务后重启
) as pool:
# 使用map方法(顺序不变)
print('--- 使用map方法 ---')
results = pool.map(process_data, data[:3])
for i, result in enumerate(results):
print(f'数据块{i}: {result}')
# 使用imap方法(惰性求值)
print('\n--- 使用imap方法 ---')
for i, result in enumerate(pool.imap(process_data, data[3:6])):
print(f'数据块{i+3}: {result}')
# 使用apply_async带回调
print('\n--- 带回调的异步执行 ---')
async_results = []
for chunk in data[6:9]:
result = pool.apply_async(
process_data,
args=(chunk,),
callback=process_callback,
error_callback=error_callback
)
async_results.append(result)
# 等待所有异步任务完成
for result in async_results:
result.wait()
print('所有数据处理完成')
五、 进程间通信(IPC)
5.1 管道(Pipe)
python
python
from multiprocessing import Process, Pipe
import time
def sender(conn):
"""发送数据的进程"""
messages = ['Hello', 'World', 'Python', 'Multiprocessing']
for msg in messages:
print(f'发送者: 发送 {msg}')
conn.send(msg)
time.sleep(0.5)
# 发送结束信号
conn.send(None)
conn.close()
def receiver(conn):
"""接收数据的进程"""
while True:
msg = conn.recv()
if msg is None:
break
print(f'接收者: 收到 {msg}')
time.sleep(0.3)
conn.close()
if __name__ == '__main__':
# 创建管道(双向)
parent_conn, child_conn = Pipe()
# 创建并启动进程
p1 = Process(target=sender, args=(child_conn,))
p2 = Process(target=receiver, args=(parent_conn,))
p1.start()
p2.start()
p1.join()
p2.join()
print('通信完成')
5.2 队列(Queue)
python
python
from multiprocessing import Process, Queue
import time
import random
def producer(queue, name):
"""生产者进程"""
for i in range(5):
item = f'{name}_产品{i}'
print(f'生产者 {name}: 生产 {item}')
queue.put(item)
time.sleep(random.uniform(0.1, 0.5))
# 发送结束信号
queue.put(None)
def consumer(queue, name):
"""消费者进程"""
while True:
item = queue.get()
if item is None:
# 再次放入None,让其他消费者也能结束
queue.put(None)
break
print(f'消费者 {name}: 消费 {item}')
time.sleep(random.uniform(0.2, 0.8))
print(f'消费者 {name}: 工作完成')
if __name__ == '__main__':
# 创建队列
queue = Queue(maxsize=10) # 最大容量10
# 创建多个生产者和消费者
producers = [
Process(target=producer, args=(queue, f'P{i}'))
for i in range(2)
]
consumers = [
Process(target=consumer, args=(queue, f'C{i}'))
for i in range(3)
]
# 启动所有进程
for p in producers:
p.start()
for c in consumers:
c.start()
# 等待生产者完成
for p in producers:
p.join()
# 添加结束信号
queue.put(None)
# 等待消费者完成
for c in consumers:
c.join()
print('所有生产消费任务完成')
六、 进程间同步
python
python
from multiprocessing import Process, Lock, Semaphore, Event, Barrier
import time
import os
# 示例1:使用锁(Lock)保护共享资源
def worker_with_lock(lock, worker_id):
"""使用锁同步的工人"""
with lock:
print(f'工人 {worker_id} (PID: {os.getpid()}) 开始工作')
time.sleep(1)
print(f'工人 {worker_id} (PID: {os.getpid()}) 结束工作')
# 示例2:使用信号量(Semaphore)控制并发数
def worker_with_semaphore(sem, worker_id):
"""使用信号量控制并发"""
with sem:
print(f'工人 {worker_id} 获取到许可证,开始工作')
time.sleep(2)
print(f'工人 {worker_id} 工作完成,释放许可证')
# 示例3:使用事件(Event)协调多个进程
def waiter(event, waiter_id):
"""等待事件的进程"""
print(f'等待者 {waiter_id} 正在等待事件...')
event.wait()
print(f'等待者 {waiter_id} 检测到事件,开始工作')
def setter(event, setter_id, delay):
"""设置事件的进程"""
time.sleep(delay)
print(f'设置者 {setter_id} 设置事件')
event.set()
# 示例4:使用屏障(Barrier)同步多个进程
def worker_at_barrier(barrier, worker_id):
"""在屏障处同步的工人"""
print(f'工人 {worker_id} 到达集合点')
time.sleep(worker_id) # 模拟不同的到达时间
barrier.wait()
print(f'工人 {worker_id} 通过屏障,继续前进')
if __name__ == '__main__':
print('=== 示例1:锁同步 ===')
lock = Lock()
processes = []
for i in range(3):
p = Process(target=worker_with_lock, args=(lock, i))
processes.append(p)
p.start()
for p in processes:
p.join()
print('\n=== 示例2:信号量控制 ===')
sem = Semaphore(2) # 允许同时2个进程工作
processes = []
for i in range(5):
p = Process(target=worker_with_semaphore, args=(sem, i))
processes.append(p)
p.start()
for p in processes:
p.join()
print('\n=== 示例3:事件协调 ===')
event = Event()
processes = []
# 创建3个等待者
for i in range(3):
p = Process(target=waiter, args=(event, i))
processes.append(p)
p.start()
# 创建设置者
setter_proc = Process(target=setter, args=(event, 0, 2))
setter_proc.start()
processes.append(setter_proc)
for p in processes:
p.join()
print('\n=== 示例4:屏障同步 ===')
barrier = Barrier(3) # 需要3个进程到达才能继续
processes = []
for i in range(3):
p = Process(target=worker_at_barrier, args=(barrier, i))
processes.append(p)
p.start()
for p in processes:
p.join()
七、进程间共享状态
7.1 共享内存(Value和Array)
python
python
from multiprocessing import Process, Value, Array
import time
def increment_counter(counter, times):
"""多个进程同时增加计数器"""
for _ in range(times):
with counter.get_lock(): # 获取锁保护
counter.value += 1
def modify_array(arr, process_id):
"""修改共享数组"""
for i in range(len(arr)):
arr[i] = process_id * 10 + i
time.sleep(0.01)
if __name__ == '__main__':
# 共享值(带锁保护)
shared_counter = Value('i', 0) # 'i' 表示整数类型
# 共享数组(带锁保护)
shared_array = Array('i', 5) # 长度为5的整数数组
print(f'初始计数器值: {shared_counter.value}')
print(f'初始数组值: {list(shared_array)}')
# 创建多个进程
processes = []
# 进程1和2:增加计数器
for i in range(2):
p = Process(target=increment_counter, args=(shared_counter, 1000))
processes.append(p)
p.start()
# 进程3和4:修改数组
for i in range(2):
p = Process(target=modify_array, args=(shared_array, i+1))
processes.append(p)
p.start()
# 等待所有进程完成
for p in processes:
p.join()
print(f'最终计数器值: {shared_counter.value}')
print(f'最终数组值: {list(shared_array)}')
7.2 服务进程(Manager)
python
python
from multiprocessing import Process, Manager
import time
def worker_modify_dict(shared_dict, worker_id):
"""修改共享字典"""
shared_dict[f'worker_{worker_id}'] = {
'pid': Process().pid,
'start_time': time.time(),
'data': list(range(worker_id * 3, worker_id * 3 + 3))
}
time.sleep(0.5)
def worker_modify_list(shared_list, worker_id):
"""修改共享列表"""
shared_list.append(f'任务_{worker_id}')
if len(shared_list) > 5:
# 注意:需要在Manager的锁保护下操作
shared_list[:] = shared_list[-5:] # 只保留最后5个
time.sleep(0.3)
if __name__ == '__main__':
with Manager() as manager:
# 创建共享数据结构
shared_dict = manager.dict()
shared_list = manager.list()
print('初始状态:')
print(f'字典: {dict(shared_dict)}')
print(f'列表: {list(shared_list)}')
# 创建多个进程操作共享数据
processes = []
# 创建字典修改进程
for i in range(3):
p = Process(target=worker_modify_dict, args=(shared_dict, i))
processes.append(p)
p.start()
# 创建列表修改进程
for i in range(5):
p = Process(target=worker_modify_list, args=(shared_list, i))
processes.append(p)
p.start()
time.sleep(0.1) # 交错启动
# 等待所有进程
for p in processes:
p.join()
print('\n最终状态:')
print(f'字典: {dict(shared_dict)}')
print(f'列表: {list(shared_list)}')
八、 实际应用示例
8.1 并行数据处理
python
python
from multiprocessing import Pool, cpu_count
import time
import math
def is_prime(n):
"""判断是否为质数(CPU密集型任务)"""
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
limit = int(math.sqrt(n)) + 1
for i in range(3, limit, 2):
if n % i == 0:
return False
return True
def process_range(range_start, range_end):
"""处理一个数字范围,统计其中的质数"""
count = 0
primes = []
for num in range(range_start, range_end):
if is_prime(num):
count += 1
primes.append(num)
return range_start, range_end, count, primes[:10] # 返回前10个质数
if __name__ == '__main__':
# 大数据范围
start_num = 1_000_000
end_num = 2_000_000
# 分割任务
num_workers = cpu_count() # 使用所有CPU核心
chunk_size = (end_num - start_num) // num_workers
ranges = []
for i in range(num_workers):
chunk_start = start_num + i * chunk_size
chunk_end = start_num + (i + 1) * chunk_size if i < num_workers - 1 else end_num
ranges.append((chunk_start, chunk_end))
print(f'使用 {num_workers} 个进程并行处理')
print(f'数字范围: {start_num} - {end_num}')
print(f'每个进程处理: {chunk_size} 个数字')
# 使用进程池并行处理
start_time = time.time()
with Pool(processes=num_workers) as pool:
# 使用starmap处理多个参数
results = pool.starmap(process_range, ranges)
# 合并结果
total_primes = 0
all_primes_sample = []
for chunk_start, chunk_end, count, primes in results:
total_primes += count
all_primes_sample.extend(primes)
print(f'范围 {chunk_start}-{chunk_end}: 找到 {count} 个质数')
elapsed_time = time.time() - start_time
print(f'\n总计找到 {total_primes} 个质数')
print(f'前20个质数示例: {sorted(all_primes_sample)[:20]}')
print(f'总耗时: {elapsed_time:.2f} 秒')
九、 注意事项和最佳实践
9.1 重要注意事项
python
python
import multiprocessing
import os
def demonstrate_issues():
print("=== 多进程编程注意事项 ===")
# 1. Windows 和 macOS 上的启动方式
if __name__ == '__main__':
print("1. 必须在 __name__ == '__main__' 保护块内启动进程")
print(" 防止子进程无限递归创建")
# 2. 进程间通信开销
print("\n2. 进程间通信有开销,应尽量减少数据传递")
print(" 大数据传输考虑使用共享内存")
# 3. 资源管理
print("\n3. 注意进程的资源消耗")
print(f" CPU核心数: {os.cpu_count()}")
print(" 创建过多进程可能导致系统资源耗尽")
# 4. 异常处理
print("\n4. 子进程异常不会自动传递到主进程")
print(" 需要在子进程内部处理异常,或通过IPC传递异常信息")
# 5. 平台差异
print("\n5. 不同平台的实现有差异:")
print(" Windows: 使用 spawn 方式启动进程")
print(" Unix/Linux: 使用 fork 方式启动进程")
if __name__ == '__main__':
demonstrate_issues()
9.2 最佳实践总结
合理设置进程数量:通常为 CPU 核心数,对于 I/O 密集型任务可适当增加
使用进程池管理:避免频繁创建销毁进程的开销
尽量减少进程间通信:通信开销可能抵消并行带来的收益
使用适当的同步机制:根据需求选择锁、信号量、事件等
正确处理异常和资源清理:确保进程正确退出,释放资源
考虑使用 concurrent.futures:更高级的接口,适合简单场景
总结
通过合理使用 multiprocessing 模块,可以充分利用多核 CPU 的优势,显著提高 Python 程序在处理 CPU 密集型任务时的性能。