Python 并发机制
在 Python 中,并发是指程序在同一时间段内处理多个任务的能力。根据任务类型和场景需求,Python 提供了多种并发实现方式。以下是对 Python 并发机制的详细解说:
1. 并发 vs 并行
- 并发(Concurrency):逻辑上同时处理多个任务(如单核 CPU 上的多任务切换)。
- 并行(Parallelism):物理上同时执行多个任务(如多核 CPU 上的多进程)。
- Python 实现:
- 多线程(Threading):受 GIL 限制,适合 IO 密集型任务。
- 多进程(Multiprocessing):突破 GIL 限制,适合 CPU 密集型任务。
- 异步编程(Asyncio):单线程内协程切换,适合高并发 IO 场景。
2. 多线程(Threading)
特点
- 轻量级,共享进程内存,适合 IO 密集型任务。
- 受 GIL 限制,无法利用多核 CPU。
示例:多线程下载图片
import threading
import requests
def download(url):
response = requests.get(url)
filename = url.split("/")[-1]
with open(filename, 'wb') as f:
f.write(response.content)
print(f"下载完成: {filename}")
urls = [
"https://picsum.photos/200/300",
"https://picsum.photos/300/400",
"https://picsum.photos/400/500"
]
# 创建并启动线程
threads = [threading.Thread(target=download, args=(url,)) for url in urls]
for t in threads:
t.start()
for t in threads:
t.join()
线程同步工具
- Lock:互斥锁,保护共享资源。
- RLock:可重入锁,允许同一线程多次获取。
- Semaphore:控制并发数量。
- Condition:线程间通信。
3. 多进程(Multiprocessing)
特点
- 每个进程有独立的 Python 解释器和内存空间,突破 GIL 限制。
- 适合 CPU 密集型任务(如科学计算、图像处理)。
示例:多进程计算
from multiprocessing import Process, Pool
import time
def cpu_task(n):
# CPU 密集型计算
return sum(i*i for i in range(n))
if __name__ == "__main__":
# 单进程
start = time.time()
results = [cpu_task(10**7) for _ in range(4)]
print(f"单进程耗时: {time.time()-start:.2f}s")
# 多进程
start = time.time()
with Pool(processes=4) as pool:
results = pool.map(cpu_task, [10**7]*4)
print(f"多进程耗时: {time.time()-start:.2f}s")
进程间通信(IPC)
- Queue:线程安全的队列,用于进程间数据传递。
- Pipe:双向通信管道。
- Manager:共享内存对象(如字典、列表)。
4. 异步编程(Asyncio)
特点
- 单线程内使用协程(coroutine)实现并发,适合高并发 IO 场景(如网络爬虫、Web 服务器)。
- 通过
async/await语法实现非阻塞 IO。
示例:异步 HTTP 请求
import asyncio
import aiohttp # 需要安装:pip install aiohttp
async def fetch(session, url):
async with session.get(url) as response:
return await response.text()
async def main():
urls = [
"https://example.com",
"https://python.org",
"https://github.com"
]
async with aiohttp.ClientSession() as session:
tasks = [fetch(session, url) for url in urls]
htmls = await asyncio.gather(*tasks)
for html in htmls:
print(f"获取 {len(html)} 字节")
asyncio.run(main())
关键概念
- 协程(Coroutine):由
async def定义的异步函数。 - 事件循环(Event Loop):调度和执行异步任务。
- Future/Promise:表示异步操作的结果。
- asyncio.sleep():非阻塞休眠,允许其他协程执行。
5. 选择合适的并发模型
| 场景 | 多线程(Threading) | 多进程(Multiprocessing) | 异步编程(Asyncio) |
|---|---|---|---|
| IO 密集型,少量任务 | ✓ | ||
| IO 密集型,高并发 | ✓ | ||
| CPU 密集型 | ✓ |
性能对比示例
import time
import threading
import asyncio
from multiprocessing import Pool
# IO 密集型任务
def io_task():
time.sleep(1) # 模拟 IO 等待
async def async_io_task():
await asyncio.sleep(1) # 非阻塞等待
# CPU 密集型任务
def cpu_task():
return sum(i*i for i in range(10**7))
# 测试 IO 密集型任务
def test_io():
N = 10
# 同步
start = time.time()
for _ in range(N):
io_task()
print(f"同步 IO 耗时: {time.time()-start:.2f}s")
# 多线程
start = time.time()
threads = [threading.Thread(target=io_task) for _ in range(N)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"多线程 IO 耗时: {time.time()-start:.2f}s")
# 异步
start = time.time()
asyncio.run(asyncio.gather(*[async_io_task() for _ in range(N)]))
print(f"异步 IO 耗时: {time.time()-start:.2f}s")
# 测试 CPU 密集型任务
def test_cpu():
N = 4
# 同步
start = time.time()
for _ in range(N):
cpu_task()
print(f"同步 CPU 耗时: {time.time()-start:.2f}s")
# 多线程
start = time.time()
threads = [threading.Thread(target=cpu_task) for _ in range(N)]
for t in threads:
t.start()
for t in threads:
t.join()
print(f"多线程 CPU 耗时: {time.time()-start:.2f}s")
# 多进程
start = time.time()
with Pool(processes=N) as pool:
pool.map(cpu_task, [None]*N)
print(f"多进程 CPU 耗时: {time.time()-start:.2f}s")
if __name__ == "__main__":
print("=== IO 密集型任务 ===")
test_io()
print("\n=== CPU 密集型任务 ===")
test_cpu()
6. 混合使用并发模型
线程池与进程池(concurrent.futures)
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
def download(url):
# 线程池适合 IO 任务
response = requests.get(url)
return response.content
def process_data(data):
# 进程池适合 CPU 任务
return len(data)
# 混合使用
with ThreadPoolExecutor(max_workers=5) as thread_pool:
results = thread_pool.map(download, urls)
with ProcessPoolExecutor(max_workers=2) as process_pool:
processed = process_pool.map(process_data, results)
异步与多进程结合
async def main():
# 异步处理大量 IO 连接
tasks = [fetch(url) for url in urls]
results = await asyncio.gather(*tasks)
# 将 CPU 密集型任务交给进程池
with ProcessPoolExecutor() as pool:
processed = await asyncio.gather(
*[asyncio.to_thread(pool.submit, process_data, r) for r in results]
)
return processed
7. 注意事项
- 线程安全:多线程操作共享资源时需使用锁机制。
- 进程开销:进程创建和切换开销大,不宜频繁创建。
- 异步兼容性:异步编程要求所有操作都是异步的(如使用
aiohttp而非requests)。 - 调试难度:并发程序的调试比同步程序复杂,需注意竞态条件和死锁。
总结
- IO 密集型任务:优先使用异步编程(asyncio),其次多线程。
- CPU 密集型任务:使用多进程(multiprocessing)。
- 高并发场景:异步编程(asyncio)是首选,适合百万级连接(如 Web 服务器、实时通信)。
合理选择并发模型可以显著提升程序性能,但需根据任务特性和系统资源进行权衡。
浙公网安备 33010602011771号