本帖最后由 littleblackLB 于 10-7-2025 16:17 编辑
简单来说,异步是一种编程模型,区别于同步,允许程序在等待 I/O 操作时继续执行其他任务,从而提高了程序的并发性和效率。
何时使用异步?对于 IO 密集型 (如文件读写、网络请求) 的任务,优先使用异步可以在提升效率基础下避免线程竞争,同时避免管理多线程的开销以减小 CPU 利用率。 对于 CPU 密集型任务,则使用多线程来并行处理任务,异步对于此类任务没有一点帮助。
定义
协程(Coroutine)协程是 asyncio 的核心概念之一。它是一个特殊的函数,可以在执行过程中暂停,并在稍后恢复执行。
协程通过 async def 关键字定义,并通过 await 关键字暂停执行,等待异步操作完成。 - import asyncio
- async def say_hello():
- print("Hello")
- await asyncio.sleep(1)
- print("World")
复制代码当函数被 async 定义后,其不再是一个普通的函数,通过调用say_hello(),会返回一个 协程对象 (Coroutine Object)。因此要执行协程,需要将其作为任务或事件注册到事件循环中(如通过await)。
await 关键字async 函数中,可以使用 await 关键字来等待一个异步操作完成。当遇到 await 时,协程函数会被暂停,等待异步操作完成后再继续执行。
事件循环在 Python 中,asyncio 仅依赖单个线程,通过事件循环的机制来并发运行多个不同任务。 尽管具体的实现很复杂,但我们可以给出大概的原理: - 协程列表 = [coro1, coro2, coro3, ...]
- while 协程列表不为空:
- 可执行的协程列表,已完成的协程列表 = 从协程列表中检查所有的协程并将'可执行'和'已完成'的协程返回
-
- for 就绪协程 in 可执行的协程列表:
- 尝试执行就绪协程
-
- for 已完成的协程 in 已完成的协程列表:
- 从协程列表中删除已完成的协程
复制代码可执行的协程列表:这个列表包含了可以立即执行的协程,也就是那些没有阻塞等待外部资源(通常是 I/O 操作)的协程。这是事件循环的关键部分,因为异步编程的主要目的之一是在等待 I/O 操作时允许其他协程执行。
基本用法运行协程要运行一个协程,你可以使用 asyncio.run() 函数。它会创建一个事件循环,并运行指定的协程。
- import asyncio
- async def main():
- print("Start")
- await asyncio.sleep(1)
- print("End")
- asyncio.run(main())
复制代码
并发执行多个任务使用 asyncio.gather() 函数并发执行多个协程,并等待它们全部完成。 - import asyncio
- async def task1():
- print("Task 1 started")
- await asyncio.sleep(1)
- print("Task 1 finished")
- async def task2():
- print("Task 2 started")
- await asyncio.sleep(2)
- print("Task 2 finished")
- async def main():
- await asyncio.gather(task1(), task2())
- asyncio.run(main())
复制代码解释: - 假设 task1 会先被执行
- 当执行到await asyncio.sleep(1)时,当前协程会中断,此时task2被执行
- 当执行到await asyncio.sleep(1)时,由于所有协程都在等待,则主线程等待
- 当task1的asyncio.sleep执行完后,重新获得执行权,执行完函数
- task2同理
输出: - Task 1 started
- Task 2 started
- Task 1 finished
- Task 2 finished
复制代码
|