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. 注意事项

  1. 线程安全:多线程操作共享资源时需使用锁机制。
  2. 进程开销:进程创建和切换开销大,不宜频繁创建。
  3. 异步兼容性:异步编程要求所有操作都是异步的(如使用 aiohttp 而非 requests)。
  4. 调试难度:并发程序的调试比同步程序复杂,需注意竞态条件和死锁。

总结

  • IO 密集型任务:优先使用异步编程(asyncio),其次多线程。
  • CPU 密集型任务:使用多进程(multiprocessing)。
  • 高并发场景:异步编程(asyncio)是首选,适合百万级连接(如 Web 服务器、实时通信)。

合理选择并发模型可以显著提升程序性能,但需根据任务特性和系统资源进行权衡。

posted @ 2025-05-10 16:42  天堂面包  阅读(38)  评论(0)    收藏  举报