Python并发编程

Python并发编程
最新回答
奥妙权志龙洗衣机

2020-10-13 09:06:29

Python并发编程可通过多线程、多进程、线程池/进程池、协程(asyncio)等方式实现,不同技术适用于不同场景,需根据需求选择合适方案。以下是具体介绍:

一、线程篇
  • 线程适用场景:线程适合处理I/O密集型任务,例如网络请求、文件读写等。由于线程间共享内存空间,通信成本较低,但需注意线程安全问题。
  • 线程实现方式

    使用threading模块创建线程,通过继承Thread类或直接实例化并传入目标函数。

    使用Lock、RLock、Semaphore等同步原语解决竞态条件,例如对共享资源的访问控制。

    示例代码结构:

    import threadinglock = threading.Lock()def task(): with lock: # 操作共享资源 passthread = threading.Thread(target=task)thread.start()
二、进程篇
  • 进程适用场景:进程适合处理CPU密集型任务,例如数学计算、图像处理等。由于进程间不共享内存,天然隔离,避免了线程安全问题,但通信成本较高。
  • 进程实现方式

    使用multiprocessing模块创建进程,支持Process类、Pool池化技术。

    通过Queue、Pipe或Manager实现进程间通信,例如传递数据或同步状态。

    示例代码结构:

    from multiprocessing import Process, Queuedef task(q): q.put("result")q = Queue()process = Process(target=task, args=(q,))process.start()print(q.get())
三、PoolExecutor篇(线程池/进程池)
  • 池化技术优势:通过复用已创建的线程或进程,减少频繁创建销毁的开销,提高资源利用率,适合批量任务处理。
  • 实现方式

    使用concurrent.futures.ThreadPoolExecutor或ProcessPoolExecutor。

    通过submit()提交任务,map()批量提交,future.result()获取结果。

    示例代码结构:

    from concurrent.futures import ThreadPoolExecutordef task(x): return x * xwith ThreadPoolExecutor(max_workers=4) as executor: results = executor.map(task, [1, 2, 3])print(list(results))
四、协程(asyncio)篇
  • 协程适用场景:协程适合高并发I/O密集型任务,例如Web爬虫、API调用等。通过单线程切换任务减少阻塞,提升吞吐量。
  • 核心概念

    事件循环(Event Loop):管理协程的执行与调度。

    协程函数(async def):定义异步任务,需通过await挂起。

    任务(Task):封装协程,提交至事件循环执行。

  • 实现方式

    使用asyncio.run()启动事件循环。

    通过asyncio.gather()并发运行多个协程。

    示例代码结构:

    import asyncioasync def fetch_data(): await asyncio.sleep(1) # 模拟I/O操作 return "data"async def main(): results = await asyncio.gather(fetch_data(), fetch_data()) print(results)asyncio.run(main())
五、协程在Web爬虫中的应用
  • 技术优势:协程可高效处理大量并发请求,避免线程切换开销,降低资源占用。
  • 实现案例

    使用aiohttp发送异步HTTP请求。

    通过asyncio.Queue管理待抓取URL队列。

    示例代码结构:

    import aiohttpimport asyncioasync def fetch(url): async with aiohttp.ClientSession() as session: async with session.get(url) as response: return await response.text()async def main(): urls = ["
    http://example.com"
    ] * 10 tasks = [fetch(url) for url in urls] results = await asyncio.gather(*tasks) print(len(results))asyncio.run(main())
六、Gevent的局限性
  • Gevent问题

    兼容性:依赖猴子补丁(monkey.patch_all())修改标准库,可能引发不可预测的副作用。

    调试难度:协程切换由底层C库控制,错误堆栈难以追踪。

    生态限制:部分第三方库未适配Gevent,导致性能下降或功能异常。

  • 替代方案:推荐使用标准库asyncio或第三方库aiohttp、asgi等。
七、并发编程选择建议
  • I/O密集型:优先选择协程(asyncio)或多线程,协程资源占用更低,线程实现更简单。
  • CPU密集型:使用多进程(multiprocessing)或进程池,避免GIL限制。
  • 混合场景:结合多进程与协程,例如主进程管理子进程,子进程内使用协程处理I/O。
(注:图片为Python之美公众号二维码,关注可获取更多技术文章)