Python的线程、进程与协程

在这里插入图片描述

一、进程和线程的比较

线程和进程是并发执行的两种主要方式

1. 定义

  • 进程:进程是操作系统分配资源的基本单位,每个进程都有独立的内存空间,包含代码、数据和系统资源。进程之间相互隔离,一个进程崩溃不会影响其他进程。
  • 线程:线程是进程内的执行单元,一个进程可以包含多个线程。线程共享进程的内存空间和资源,因此线程间的通信比进程间更高效,但也更容易出现数据竞争等问题。

2. 内存空间

  • 进程:每个进程有独立的内存空间,进程间通信需要通过IPC(Inter-Process Communication)机制,如管道、消息队列、共享内存等。
  • 线程:线程共享进程的内存空间,线程间可以直接访问共享数据,但需要同步机制(如锁)来避免竞争条件。

3. 创建和销毁

  • 进程:创建和销毁进程的开销较大,因为需要分配和回收独立的内存空间和资源。
  • 线程:创建和销毁线程的开销较小,因为它们共享进程的资源。

4. 并发性

  • 进程:由于进程间相互独立,多进程程序可以充分利用多核CPU,适合CPU密集型任务。
  • 线程:线程适合I/O密集型任务,但由于Python的GIL(全局解释器锁),多线程在CPU密集型任务中无法充分利用多核CPU。

5. 稳定性

  • 进程:进程间相互隔离,一个进程崩溃不会影响其他进程,稳定性较高。
  • 线程:线程共享内存空间,一个线程崩溃可能导致整个进程崩溃,稳定性较低。

6. 通信

  • 进程:进程间通信需要通过IPC机制,较为复杂。
  • 线程:线程间通信可以直接通过共享内存,较为简单,但需要同步机制。

7. Python中的实现

  • 进程:Python中使用multiprocessing模块来创建和管理进程。
  • 线程:Python中使用threading模块来创建和管理线程。

8. 示例代码

进程示例
import multiprocessing

def worker():
    print("Process worker")

if __name__ == "__main__":
    p = multiprocessing.Process(target=worker)
    p.start()
    p.join()
线程示例
import threading

def worker():
    print("Thread worker")

t = threading.Thread(target=worker)
t.start()
t.join()

小结

  • 进程适合需要高隔离性和CPU密集型任务。
  • 线程适合I/O密集型任务和需要高效通信的场景。

选择使用进程还是线程,取决于具体的应用场景和需求。

二、协程

参考1:https://liaoxuefeng.com/books/python/async-io/coroutine/index.html
参考2:https://liaoxuefeng.com/books/python/async-io/asyncio/index.html

协程(Coroutine) 是一种特殊的函数,能够在执行过程中暂停(挂起)并在稍后恢复执行。它是实现异步编程的核心机制之一,常用于处理 I/O 密集型任务(如网络请求、文件读写等),避免阻塞主线程。

协程与普通函数的区别在于:

  • 普通函数从开始执行到结束,不会中途暂停。
  • 协程可以在执行过程中暂停(通过 awaityield),并在适当的时候恢复执行。

1. 协程的关键特性

  1. 可暂停和恢复
    协程可以在执行过程中暂停,将控制权交还给调用者,稍后再从暂停的地方恢复执行。

  2. 异步非阻塞
    协程通常与异步 I/O 操作结合使用,避免阻塞主线程,提高程序的并发性能。

  3. 轻量级
    协程的上下文切换开销比线程小得多,适合高并发场景。


2. 协程的工作原理

  1. 事件循环(Event Loop)
    协程的运行依赖于事件循环。事件循环负责调度协程的执行,当协程遇到 await 时,事件循环会暂停当前协程,转而执行其他任务。

  2. 挂起和恢复
    当协程遇到 await 时,它会挂起自己,并将控制权交还给事件循环。事件循环会在异步操作完成后恢复协程的执行。


3. 协程与生成器的关系

协程的实现基于 Python 的生成器(Generator)。生成器通过 yield 关键字实现暂停和恢复,而协程通过 await 实现类似的功能。不过,协程更专注于异步编程,而生成器更常用于惰性计算。


4. 协程的适用场景

  1. I/O 密集型任务
    如网络请求、数据库操作、文件读写等。
  2. 高并发应用
    如 Web 服务器、爬虫、实时数据处理等。
  3. 异步编程
    需要避免阻塞主线程的场景。

5. Python 中的协程

在 Python 中,协程通过 asyncawait 关键字实现:

  • async def:定义一个协程函数。
  • await:暂停协程的执行,等待异步操作完成。
示例
import asyncio

async def say_hello():
    print("Hello")
    await asyncio.sleep(1)  # 模拟 I/O 操作,暂停 1 秒
    print("World")

# 运行协程
asyncio.run(say_hello())
结果
Hello!
(等待约1秒)
World!

6. 示例:并发执行多个协程

import asyncio

async def io_operation(name, delay):
    print(f"IO操作 {
      
      name} 开始")
    await asyncio.sleep(delay)
    print(f"IO操作 {
      
      name} 完成")
    return f"{
      
      name} 的结果"

async def compute_operation(name, delay):
    print(f"计算操作 {
      
      name} 开始")
    await asyncio.sleep(delay)  # 模拟耗时计算
    print(f"计算操作 {
      
      name} 完成")
    return f"{
      
      name} 的计算结果"

async def network_operation(name, delay):
    print(f"网络操作 {
      
      name} 开始")
    await asyncio.sleep(delay)  # 模拟网络请求
    print(f"网络操作 {
      
      name} 完成")
    return f"{
      
      name} 的网络响应"

# 设计task中有多个协程
async def task(name, delay):
    print(f"Task {
      
      name} 启动")
    
    # 并发执行多个异步操作
    results = await asyncio.gather(
        io_operation(f"{
      
      name}-IO", delay),
        compute_operation(f"{
      
      name}-Compute", delay/2),
        network_operation(f"{
      
      name}-Network", delay*1.5)
    )
    
    # 串行执行一些操作
    result2 = await io_operation(f"{
      
      name}-Sequential", 10) # delay/3
    result3 = await compute_operation(f"{
      
      name}-Final", delay/4)
    
    print(f"Task {
      
      name} 完成,获得结果:{
      
      results}, {
      
      result2}, {
      
      result3}")
执行一
async def main():
    # 并发执行多个协程
    await asyncio.gather(
        task("A", 2),
        task("B", 1),
        task("C", 3),
    )
if __name__ == "__main__":
    asyncio.run(main())
返回一
Task A 启动
Task B 启动
Task C 启动
IO操作 A-IO 开始
计算操作 A-Compute 开始
网络操作 A-Network 开始
IO操作 B-IO 开始
计算操作 B-Compute 开始
网络操作 B-Network 开始
IO操作 C-IO 开始
计算操作 C-Compute 开始
网络操作 C-Network 开始
计算操作 B-Compute 完成
计算操作 A-Compute 完成
IO操作 B-IO 完成
网络操作 B-Network 完成
计算操作 C-Compute 完成
IO操作 B-Sequential 开始
IO操作 A-IO 完成
网络操作 A-Network 完成
IO操作 C-IO 完成
IO操作 A-Sequential 开始
网络操作 C-Network 完成
IO操作 C-Sequential 开始
IO操作 B-Sequential 完成
计算操作 B-Final 开始
计算操作 B-Final 完成
Task B 完成,获得结果:['B-IO 的结果', 'B-Compute 的计算结果', 'B-Network 的网络响应'], B-Sequential 的结果, B-Final 的计算结果
IO操作 A-Sequential 完成
计算操作 A-Final 开始
计算操作 A-Final 完成
Task A 完成,获得结果:['A-IO 的结果', 'A-Compute 的计算结果', 'A-Network 的网络响应'], A-Sequential 的结果, A-Final 的计算结果
IO操作 C-Sequential 完成
计算操作 C-Final 开始
计算操作 C-Final 完成
Task C 完成,获得结果:['C-IO 的结果', 'C-Compute 的计算结果', 'C-Network 的网络响应'], C-Sequential 的结果, C-Final 的计算结果
执行二
async def main():
    tasks = [task("A", 2), task("B", 1), task("C", 3)]
    for i in range(3):
        print(f"Execute Task {
      
      i} started")
        task_return = asyncio.create_task(tasks[i % len(tasks)])
        await asyncio.wait_for(task_return, timeout=300)
        print(f"Execute Task {
      
      i} completed")

if __name__ == "__main__":
    asyncio.run(main())
返回二
Execute Task 0 started
Task A 启动
IO操作 A-IO 开始
计算操作 A-Compute 开始
网络操作 A-Network 开始
计算操作 A-Compute 完成
IO操作 A-IO 完成
网络操作 A-Network 完成
IO操作 A-Sequential 开始
IO操作 A-Sequential 完成
计算操作 A-Final 开始
计算操作 A-Final 完成
Task A 完成,获得结果:['A-IO 的结果', 'A-Compute 的计算结果', 'A-Network 的网络响应'], A-Sequential 的结果, A-Final 的计算结果
Execute Task 0 completed
Execute Task 1 started
Task B 启动
IO操作 B-IO 开始
计算操作 B-Compute 开始
网络操作 B-Network 开始
计算操作 B-Compute 完成
IO操作 B-IO 完成
网络操作 B-Network 完成
IO操作 B-Sequential 开始
IO操作 B-Sequential 完成
计算操作 B-Final 开始
计算操作 B-Final 完成
Task B 完成,获得结果:['B-IO 的结果', 'B-Compute 的计算结果', 'B-Network 的网络响应'], B-Sequential 的结果, B-Final 的计算结果
Execute Task 1 completed
Execute Task 2 started
Task C 启动
IO操作 C-IO 开始
计算操作 C-Compute 开始
网络操作 C-Network 开始
计算操作 C-Compute 完成
IO操作 C-IO 完成
网络操作 C-Network 完成
IO操作 C-Sequential 开始
IO操作 C-Sequential 完成
计算操作 C-Final 开始
计算操作 C-Final 完成
Task C 完成,获得结果:['C-IO 的结果', 'C-Compute 的计算结果', 'C-Network 的网络响应'], C-Sequential 的结果, C-Final 的计算结果
Execute Task 2 completed

7. 协程(asyncio)的应用场景(agent)

7.1. 高并发 I/O 密集型场景

Agent 的核心功能通常涉及大量 网络请求异步事件处理,例如:

  • 多服务调用:同时访问 LLM(如 OpenAI API)、数据库、知识库、工具插件等。
  • 实时流式响应:处理 WebSocket 消息、长轮询或 SSE(Server-Sent Events)。
  • 并行任务:例如同时监控多个输入源(用户消息、传感器数据、API 推送)。

asyncio 的优势
用单线程即可管理数千个并发连接,避免多线程的上下文切换开销(GIL 限制下,Python 多线程并不适合高并发 I/O)。

7.2. 事件驱动的编程模型

Agent 的本质是 响应事件(如用户输入、系统触发、定时任务),而 asyncio 的事件循环机制天然适合这种模式:

  • 事件循环 监听多种输入源(HTTP 请求、消息队列、用户交互)。
  • 协程 按需挂起和恢复,例如等待 LLM 生成响应时,可以处理其他用户请求。

典型案例
一个聊天 Agent 在等待 GPT-4 生成回复时,可以同时处理其他用户的查询,而非阻塞排队。

7.3. 与异步生态的兼容性

现代 Python 异步库(如 aiohttpasyncpgFastAPI)普遍基于 asyncio 设计。Agent 需要集成这些组件时,asyncio 提供统一接口:

  • 数据库访问:异步 ORM(如 tortoise-orm)或驱动(如 asyncpg)。
  • 网络通信:HTTP 客户端(aiohttp)、WebSocket(websockets)。
  • 消息队列:Kafka(aiokafka)、RabbitMQ(aio-pika)。
7.4. 轻量级与资源效率
  • 对比多线程/多进程
    Agent 可能需长期运行(如后台服务),协程的内存占用远低于线程(一个线程约 8MB 栈内存,而协程仅 KB 级)。
  • 快速启动
    动态创建数千个协程(如处理突发流量)比线程/进程更高效。
7.5. 流式处理与实时性

Agent 常需处理 流式数据(如逐字输出 LLM 响应、实时日志监控)。asyncio 的异步生成器(async for)可优雅实现:

async def stream_llm_response(query):
    async with aiohttp.ClientSession() as session:
        async with session.post("https://api.openai.com/...", json={
    
    "query": query}) as resp:
            async for chunk in resp.content:  # 流式读取
                yield chunk.decode()
7.6 不适用 asyncio 的 Agent 场景
  • CPU 密集型任务
    例如本地模型推理(需用 multiprocessing 或分离为微服务)。
  • 依赖同步阻塞库
    某些传统库(如 requestspsycopg2)会阻塞事件循环,需通过 run_in_executor 隔离。
小结:为什么 Agent 偏爱 asyncio
需求 asyncio 的解决方案
高并发 I/O 单线程事件循环 + 协程挂起
事件驱动 原生支持异步回调与任务调度
集成现代异步库 生态一致性(HTTP/DB/MQ 等)
低资源消耗 轻量级协程替代线程
流式/实时响应 异步生成器与 await 非阻塞等待

典型案例

总结

协程是一种轻量级的并发编程工具,能够暂停和恢复执行,适合处理 I/O 密集型任务和高并发场景。在 Python 中,协程通过 asyncawait 实现,是异步编程的核心机制之一。

∼ O n e   p e r s o n   g o   f a s t e r ,   a   g r o u p   o f   p e o p l e   c a n   g o   f u r t h e r ∼ \sim_{One\ person\ go\ faster,\ a\ group\ of\ people\ can\ go\ further}\sim One person go faster, a group of people can go further

猜你喜欢

转载自blog.csdn.net/ThomasCai001/article/details/146403903
今日推荐