协程是一种用户态内的上下文切换技术,允许在单线程中实现并发操作。Python中的asyncio库提供了对协程的支持,包括定义协程函数、创建协程对象、使用await表达式等。事件循环是asyncio的核心,负责管理和调度协程的执行。Task对象用于封装协程,Future对象用于表示异步操作的结果。asyncio还提供了run函数、wait函数、run_in_executor函数等API,用于执行协程、等待任务完成和在执行器中运行函数。异步迭代器和异步上下文管理器也得到了支持。uvloop是asyncio事件循环的替代方案,提供了更高的性能。
协程 Coroutine
也称之为微线程,是一种用户态内的上下文切换技术。
简而言之,就是一种技术可以使得在一个线程的情况下实现代码快互相切换执行
以下代码中,假定#switch函数是用于实现这样的一种功能(用户态内的上下文切换)
asyncio的优势
其优势在于它可以做到遇到IO阻塞情况下可自动进行切换
定义
协程函数:
协程对象 (Coroutine Object) awaitable:
“
async” defines a coroutine“
await” suspends or yields execution to another coroutine from within a coroutine.Awaitables
We say that an object is an awaitable object if it can be used in an
awaitexpression. Many asyncio APIs are designed to accept awaitables.There are three main types of awaitable objects: coroutines, Tasks, and Futures.
事件循环
可执行的协程列表:这个列表包含了可以立即执行的协程,也就是那些没有阻塞等待外部资源(通常是 I/O 操作)的协程。这是事件循环的关键部分,因为异步编程的主要目的之一是在等待 I/O 操作时允许其他协程执行。(ChatGPT)
协程函数的特性:
一个协程函数中可以有多个await协程对象/Task对象/Future对象
协程标志: 使用
async关键字定义的函数被标记为协程函数。这意味着这些函数可以在执行过程中被暂停和恢
复,以允许其他协程执行。await表达式: 在async函数中,可以使用await关键字来等待一个异步操作完成。当遇到await时
,协程函数会被暂停,等待异步操作完成后再继续执行。非阻塞式:
async函数的特性使其在等待I/O操作等异步任务时不会阻塞整个程序的执行,而是可以切换到其他
协程,从而提高并发性能。生成器语法:
async函数的定义方式类似于生成器函数,使用async def关键字,而函数中的yield语句则变成了await。异步编程库支持:
async函数通常与异步编程库(如asyncio、Trio等)一起使用,这些库提供了管理、
调度和执行协程的机制。返回协程对象: 调用
async函数会返回一个协程对象,而不是立即执行函数体。要执行协程,需要将其作为任
务或事件注册到事件循环中。异常处理: 协程函数中的异常会传播回调用者。可以使用
try和except来捕获和处理协程函数中的异常。并发性:
async函数允许在同一个线程中处理多个协程,从而在单线程中实现并发操作。这对于I/O密集型操作
非常有用。
Task对象
Tasks are used to schedule coroutines concurrently.
When a coroutine is wrapped into a Task with functions like
asyncio.create_task()the coroutine is automatically scheduled to run soon
在事件循环中添加多个任务的
除了使用asyncio.create_task()函数(在Python 3.7中被加入)以外,还可以用低层级的loop.create_task()或ensure_future()函数。不建议手动实例化Task对象。
常用写法
由
run函数直接获取事件循环
手动获取事件循环
asyncio.Future对象
A
Futureis a special low-level awaitable object that represents an eventual result of an asynchronous operation.
一个对象用来忙等待
Future内部具有_state_属性,当其状态为已完成的时候,则调用其的await表达式将停止阻塞
Task继承Future,Task对象内部await结果的处理基于Future对象
深入理解事件循环
情况1:
total ≈ 3秒
情况2:
total ≈ 5秒
原因:
情况1:
当执行到
await t时,main()协程对象开始阻塞。事件循环执行按照任务列表中,切换上下文开始执行
set_after(fut)协程对象,执行到await asyncio.sleep(2)时,该协程对象开始阻塞当2秒过去后,
set_after(fut)执行完fut.set_result(114514)后结束,协程对象完成,t任务完成await t停止阻塞,...await fut由于已经有结果,所以取操作耗时约为0最后为3秒
情况2:
...
执行到
await fut阻塞事件循环,切换上下文开始执行
set_after(fut)协程对象...
最后为5秒
常用API
asyncio.run(coro, *, debug=None)
Execute the coroutine coro and return the result.
This function runs the passed coroutine, taking care of managing the asyncio event loop, finalizing asynchronous generators, and closing the threadpool.
This function cannot be called when another asyncio event loop is running in the same thread.
If debug is
True, the event loop will be run in debug mode.Falsedisables debug mode explicitly.Noneis used to respect the global Debug Mode settings.This function always creates a new event loop and closes it at the end. It should be used as a main entry point for asyncio programs, and should ideally only be called once.
coroutine asyncio.wait(aws, *, timeout=None, return_when=ALL_COMPLETED)
Run
FutureandTaskinstances in the aws iterable concurrently and block until the condition specified by return_when.aws列表中可以为
Future对象 或Task对象 或协程对象The aws iterable must not be empty and generators yielding tasks are not accepted.
Returns two sets of Tasks/Futures:
(done, pending).可以是协程对象的原因:
loop.rununtil_complete(_future)
Run until the future (an instance of
Future) has completed.If the argument is a coroutine object it is implicitly scheduled to run as a
asyncio.Task.Return the Future’s result or raise its exception.
awaitable loop.runin_executor(_executor, func, *args)
Arrange for func to be called in the specified executor.
The executor argument should be an
concurrent.futures.Executorinstance. The default executor is used if executor isNone.
asyncio + 不支持异步的模块
结合多线程库中的线程池等效使用异步
不同点:
底层使用的是线程池,原理不同,因此为多线程操作,更耗费资源
而原始asyncio为单线程操作
Implemented method:
案例 Case
异步迭代器
Asynchronous Iterators and “async for”
An asynchronous iterable is able to call asynchronous code in its iter implementation, and asynchronous iterator can call asynchronous code in its next method. To support asynchronous iteration:
An object must implement an
__aiter__method (or, if defined with CPython C API,tp_as_async.am_aiterslot) returning an asynchronous iterator object.An asynchronous iterator object must implement an
__anext__method (or, if defined with CPython C API,tp_as_async.am_anextslot) returning an awaitable.To stop iteration
__anext__must raise aStopAsyncIterationexception.
异步上下文管理器
Asynchronous Context Managers and “async with”
An asynchronous context manager is a context manager that is able to suspend execution in its enter and exit methods.
To make this possible, a new protocol for asynchronous context managers is proposed. Two new magic methods are added: __aenter__ and __aexit__. Both must return an awaitable.
uvloop
是asyncio事件循环的替代方案
pip3 install uvloop