【Python面试题】进程、线程、协程与事件循环

最近在学习ASGI(Asynchronous Server Gateway Interface)的时候,看到一句话:
“ASGI的并发 ≈ 一个event loop 同时挂起的协程数”
所以,进程,线程,协程到底是什么关系?事件循环又是什么东西?

一、进程、线程、协程的区别

  1. 进程(process)
  • 定义:操作系统分配资源(CPU时间、内存空间等)的最小单位。
  • 特点:
    • 每个进程都有自己独立的内存空间。
    • 进程之间相互独立,通信要靠管道、消息队列、共享内存
    • 开销大,切换成本高
  • 类比:一台太独立的电脑,各自运行一套系统
  1. 现成(Thread)
  • 定义:CPU调度的最小单位,一个进程里面可以有多个线程
  • 特点
    • 共享同一个进程内的内存资源
    • 切换成本比进程小
    • 容易出现数据竞争,需要枷锁
  • 类比:同一台电脑里的多个应用窗口,公用内存和硬盘,但干活各自分开
  1. 协程(coroutine)
  • 定义:用户态的“轻量级线程”,有程序自己调度。
  • 特点
    • 通过async/await 来挂起和恢复执行。
    • 单线程内实现高并发。
    • 成本绩效,可以轻松支撑成千上万任务
    • 缺点:本身不能利用多核(需要结合多进程)
  • 类比:浏览器里面一个窗口的多个标签页,虽然只有一个窗口线程,但是你能快速切换任务,感觉像是并发的。

4.三者对比

特性进程线程协程
调度者操作系统操作系统程序自身(时间循环)
内存空间独立共享进程内存共享线程内存
切换开销极小
并发数量少(几十~上百)中(中~几千)多(几万+)
多核利用×(需多进程)

二、层级关系梳理

很多人会误解成:进程⊃线程⊃协程
其实更精准的表述是:

进程
 ├── 线程1
 │     └── (事件循环1)
 │            ├── 协程a
 │            ├── 协程b
 │            └── 协程c
 ├── 线程2
 │     └── (事件循环2)
 │            ├── 协程d
 │            └── 协程e
 └── 线程3
       └── (可能没有事件循环,只跑普通任务)

在Python的ASGI框架(如FastApi+uvicorn)里,最常见的模式就是:一个进程,一个线程,一个事件循环,上万协程

三、什么是时间循环

  1. 定义

时间循环(event loop)就是一个调度器
他会不断循环,检查哪些协程准备好执行,然后把CPU时间分配给他们。
协程在await I/O时,会主动把执行权交还给时间循环;I/O完成后,loop会把任务切回来继续执行。

  1. 类比

传统服务员:
传统阻塞:服务员全城只能盯着一个客人
事件循环:点完单就去服务下一个客人,等厨房喊菜在回来继续。
一个人看似能同时服务很多客人

  1. 示例
async def task(name, sec):
    print(f"{name} 开始")
    await asyncio.sleep(sec)
    print(f"{name} 结束")


async def main():
    await asyncio.gather(
        task("任务1", 1),
        task("任务2", 2),
        task("任务3", 3),
        task("任务4", 4),
    )


asyncio.run(main())

#输出结果

任务1 开始
任务2 开始
任务3 开始
任务4 开始
任务1 结束
任务2 结束
任务3 结束
任务4 结束

四、怎么启动

  1. 启动一个线程
import threading

def worker():
    print("启动了一个线程")

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


  1. 启动一个进程

from multiprocessing import Process

def worker():
    print("启动了一个进程")

if __name__ == "__main__":
    p = Process(target=worker)
    p.start()
    p.join()

  1. 启动一个事件循环(在线程里)
import threading, asyncio

async def coro():
    await asyncio.sleep(1)
    print("协程完成")


def loop_thread():
    loop = asyncio.new_event_loop()
    asyncio.set_event_loop(loop)
    loop.run_forever()

t = threading.Thread(target=loop_thread)
t.start()
  1. 启动一个协程
import asyncio

async def task():
    await asyncio.sleep(1) 

    return "ok"

async def main():
    result = await task()
    print(result)

asyncio.run(main())

五、总结

进程:操作系统资源单位,重量级
线程:CPU调度单位,共享进程内存
协程:轻量级任务,由事件循环调度
事件循环:线程里的调度器,管理协程的挂起和恢复
在Python的async/await的世界里面:

  • 协程是主角,事件循环是导演,线程是舞台,进程是一整栋剧院

而ASGI的高并发,本质就是:一个事件循环里同时挂起成千上万个协程

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值