异步函数
异步函数:使用async def
定义的函数称为异步函数。这些函数返回一个协程对象,而不是立即执行。
async def my_async_function():
# Some asynchronous operations
pass
协程:协程是可以在某些操作(如I/O操作)完成之前暂停并在之后恢复的函数。协程对象是通过调用异步函数得到的。
await
关键字
await
用于等待一个可等待对象(awaitable),通常是一个协程或另一个异步操作。await
会暂停当前异步函数的执行,允许其他任务(异步函数)在此期间运行。当被等待的操作完成时,异步函数会恢复执行,并返回结果。
使用 await
的好处在于,它允许异步函数在等待某些操作(如I/O操作)完成时不阻塞整个程序。这种机制使得多个异步操作可以并发执行,从而提高程序的效率和响应速度。
非阻塞IO
阻塞IO
事件循环阻塞:事件循环是异步编程的核心,它通过调度和执行协程来管理任务。如果一个阻塞I/O操作被引入异步函数中,事件循环将被阻塞,无法执行其他任务。
并发性能降低:异步编程的优势在于能够并发执行大量I/O密集型任务。如果引入阻塞操作,这种优势会被消除,程序的并发性能会大大降低。
import asyncio
import time
# 一个异步函数,包含阻塞操作
async def async_function_with_blocking_io():
print("Start blocking I/O operation")
time.sleep(5) # 阻塞操作
print("End blocking I/O operation")
# 主函数,启动事件循环
async def main():
await async_function_with_blocking_io()
# 运行主函数
asyncio.run(main())
在这个示例中,async_function_with_blocking_io
是一个异步函数,但它包含一个阻塞的time.sleep(5)
操作。运行这个程序,你会发现:
"Start blocking I/O operation" 会立即打印。
程序会暂停5秒钟,因为
time.sleep(5)
是一个阻塞操作。5秒钟后,"End blocking I/O operation" 会打印。
在这5秒钟内,事件循环被阻塞,无法执行其他异步任务。
import asyncio
import time
# 一个阻塞操作
def blocking_io_operation():
print("Start blocking I/O operation")
time.sleep(5) # 阻塞操作
print("End blocking I/O operation")
# 一个异步函数,使用线程池执行阻塞操作
async def async_function_with_blocking_io():
await asyncio.to_thread(blocking_io_operation)
# 主函数,启动事件循环
async def main():
await async_function_with_blocking_io()
# 运行主函数
asyncio.run(main())
async_function_with_blocking_io
使用 await asyncio.to_thread(blocking_io_operation)
将阻塞操作移到线程池中执行,从而保证允许其他异步任务并发执行,不阻塞事件循环
方法
框架
Fastapi
1.同步路由:当你定义一个同步路由(即没有使用async def定义的函数),FastAPI会在一个线程池中运行这个函数。这意味着,如果你的函数中有阻塞的I/O操作(例如,读写文件或数据库查询),这个操作会阻塞当前的线程,但不会阻塞事件循环。事件循环可以继续处理其他任务。这是因为每个同步任务都在自己的线程中运行,所以一个任务的阻塞不会影响其他任务。
2.异步路由:当你定义一个异步路由(即使用async def定义的函数),FastAPI会直接调用这个函数,并期望它执行非阻塞的I/O操作。因为在异步编程中,await关键字会将控制权交还给事件循环,事件循环可以继续处理其他任务,直到异步操作完成。如果你在异步路由中执行阻塞的I/O操作,那么你就违反了这个约定,事件循环会被阻塞,无法处理其他任务,这会降低应用的性能。
总的来说,FastAPI通过在线程池中运行同步路由,并期望异步路由只执行非阻塞的I/O操作,来确保应用的高性能和响应能力
评论区