进程线程和协程三

前言

我们前面讲了进程、线程,今天引入另外一个概念协程

协程

先上一下结论哈,普通函数前面加了一个async就是协程

示例代码

python 复制代码
#使用async来修饰普通函数
async def main(): 
    print("test")
    return "test"


if __name__ == "__main__":
    print(type(main()))

打印结果

我们先不管这个报错,先看print的type数据 打印出来的 <class 'coroutine'> ,这个就是协程的意思

如何执行协程函数

python 复制代码
async def main():
    print("test")
    return "test"


if __name__ == "__main__":
    print(type(main()))
    main()

我在上面代码的基础上加了一个 main()方式看下能否像普通函数一样去执行

打印结果

发现除了打印了类型外main执行直接就报错了: RuntimeWarning: coroutine 'main' was never awaited main() RuntimeWarning: Enable tracemalloc to get the object allocation traceback 报错的信息是协程没有使用await ,那我们加上await

引入await

python 复制代码
async def test():
    print("test")
    return "test"


async def main():
    await test()


if __name__ == "__main__":
    print(type(main()))
    main()

我们代码进行了一次转换一开始main的功能放到了test函数里面,又额外定义了一个main来await新的test

打印结果

发现还是报和刚才一样的错误,这是因为main()这个协程函数执行的时候也没加await ,但是await有必须得在async定义的协程函数里 ,这个if怎么加async呢

引入asyncio

python 复制代码
import asyncio


async def test():
    print("test")
    return "test"


async def main():
    await test()


if __name__ == "__main__":
    print(type(main()))
    asyncio.run(main())

打印结果

结论

  • async定义的普通函数就是协程函数
  • 协程函数执行前面必须加await
  • async和await相互依存,相互依赖

多个协程

刚才上面代码是单个协程执行时候的场景,但是如果我们存在多个协程执行点时候呢?

示例代码

python 复制代码
import asyncio


async def test(name: str, age: int):
    print("name{}, age{}".format(name, age))
    return f"当前协程是{name},年龄{age}"


async def main():
    print("task1开始执行")
    task1 = asyncio.create_task(test("小刘", 28))
    print("task2开始执行")
    task2 = asyncio.create_task(test("小孔", 38))

    result1 = await task1
    print("await task1执行结束")

    result2 = await task2
    print("await task2执行结束")

    print("result1执行结果是:", result1)
    print("result2执行结果是:", result2)


if __name__ == "__main__":
    asyncio.run(main())

代码解释:我们通过create_task创建了多个要执行的协程任务(task)然后在分别获取每个协程的结果

打印结果

我们发现再打印await task1执行结束结束前,协程task1已经执行结束,task2也是这样的,所以我们得出结论

结论

-create_task创建协程任务的时候,就会开始执行协程了 -执行协程不会阻塞主任务 -碰到await会阻塞当前的协程

执行流程

text 复制代码
asyncio.run(main())
        │
        ▼
    事件循环启动
        │
        ▼
    执行 main 协程
        │
        ▼
create_task(test1) ──────────────────┐
create_task(test2) ──────────────────┤
                                     │
                                     ▼
                             事件循环调度队列
                              [task1, task2]
                                     │
                                     ▼
    ┌─────────────────────────────────────────────────────┐
    │                    事件循环开始调度                    │
    └─────────────────────────────────────────────────────┘
                                     │
            ┌────────────────────────┴────────────────────────┐
            ▼                                                 ▼
    ╔════════════════════╗                            ╔════════════════════╗
    ║    执行 test1      ║                            ║    执行 test2      ║
    ║         │          ║                            ║         │          ║
    ║    [遇到await]      ║                            ║    [遇到await]      ║
    ║         │          ║                            ║         │          ║
    ║   交出CPU控制权      ║                            ║   交出CPU控制权      ║
    ╚════════════════════╝                            ╚════════════════════╝
            │                                                  │
            └──────────────────┬───────────────────────────────┘
                               ▼
                    ┌─────────────────────┐
                    │  事件循环重新调度     │
                    │  检查谁可以继续执行   │
                    └─────────────────────┘
                               │
            ┌──────────────────┴──────────────────┐
            │                                      │
            ▼                                      ▼
    ╔════════════════════╗                ╔════════════════════╗
    ║  被事件循环唤醒     ║                ║  被事件循环唤醒     ║
    ║         │          ║                ║         │          ║
    ║         ▼          ║                ║         ▼          ║
    ║  test2执行完成      ║                ║  test1执行完成      ║
    ╚════════════════════╝                ╚════════════════════╝
            │                                      │
            └──────────────────┬───────────────────┘
                               ▼
                    ┌─────────────────────┐
                    │  所有任务执行完成    │
                    └─────────────────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │  返回 main 协程      │
                    └─────────────────────┘
                               │
                               ▼
                    ┌─────────────────────┐
                    │   关闭事件循环       │
                    └─────────────────────┘

关键点

  • 遇到await:协程主动交出控制权给事件循环
  • 睡眠期间:协程被挂起,不占用CPU
  • 唤醒机制:时间到了,事件循环把协程重新放回就绪队列
  • 调度顺序:谁先醒来谁先执行(test2先醒,所以先结束)

引入gather

python 复制代码
import asyncio


async def test(name: str, age: int):
    print("name{}, age{}".format(name, age))
    return f"当前协程是{name},年龄{age}"


async def main():
    print("task1开始执行")
    task1 = asyncio.create_task(test("小刘", 28))
    print("task2开始执行")
    task2 = asyncio.create_task(test("小孔", 38))
    # gather 返回一个 awaitable 对象
    result = await asyncio.gather(task1, task2)
    for task in result:
        print(task)


if __name__ == "__main__":
    asyncio.run(main())

示例结果

引入as_completed

python 复制代码
import asyncio


async def test(name: str, age: int):
    print("name{}, age{}".format(name, age))
    return f"当前协程是{name},年龄{age}"


async def main():
    print("task1开始执行")
    task1 = asyncio.create_task(test("小刘", 28))
    print("task2开始执行")
    task2 = asyncio.create_task(test("小孔", 38))

    for coro in asyncio.as_completed([task1, task2]):
        result = await coro
        print(result)


if __name__ == "__main__":
    asyncio.run(main())

示例结果

gather和as_completed的区别

相关推荐
感性的程序员小王2 小时前
我做了个 AI + 实时协作 的 draw.io,免费开源!!
前端·后端
那我掉的头发算什么2 小时前
【SpringBoot】统一功能处理详解
java·spring boot·后端·spring
rlpp2 小时前
spring.profiles.active和spring.profiles.include的使用及区别说明
java·后端·spring
小小张说故事3 小时前
Python图像处理利器:Pillow (PIL)入门指南
后端·python·图像识别
DevDengChao3 小时前
[Aliyun] [FC] 如何使用 website-fc-serve 插件部署静态网站
前端·后端
大魔王7193 小时前
进程线程和协程二
后端
前端拿破轮3 小时前
利用Github Page + Hexo 搭建专属的个人网站(一)
前端·人工智能·后端
鱼人3 小时前
线上排障利器:10 个必备 Linux 命令快速定位日志中的 Bug
后端