首页 > 后端开发 > Python教程 > 高性能 Python:Asyncio

高性能 Python:Asyncio

Susan Sarandon
发布: 2025-01-01 06:30:09
原创
951 人浏览过

High-Performance Python: Asyncio

并发编程是一种处理多个任务同时执行的编程方法。在Python中,asyncio是实现异步编程的强大工具。基于协程的概念,asyncio 可以高效地处理 I/O 密集型任务。本文将介绍asyncio的基本原理和使用方法

High-Performance Python: Asyncio

为什么我们需要 asyncio

我们知道,在处理I/O操作时,使用多线程相比普通的单线程可以大大提高效率。那么,为什么我们还需要 asyncio?

多线程有很多优点,应用广泛,但也有一定的局限性:

  • 例如多线程的运行过程很容易被中断,因此可能会出现race condition的情况。
  • 而且,线程切换本身是有一定成本的,线程数不可能无限增加。因此,如果你的I/O操作非常繁重,多线程很可能无法满足高效率和高质量的要求。

正是为了解决这些问题,asyncio 应运而生。

同步 VS 异步

我们先区分一下Sync(同步)和Async(异步)的概念。

  • 同步是指操作依次执行。前一个操作完成后才能执行下一个操作。
  • 异步意味着不同的操作可以交替执行。如果其中一项操作被阻塞,程序不会等待,而是会寻找可执行的操作来继续。

异步如何工作

  1. 协程:asyncio 使用协程来实现异步操作。协程是使用 async 关键字定义的特殊函数。在协程中,await 关键字可用于暂停当前协程的执行并等待异步操作完成。
  2. 事件循环:事件循环是asyncio的核心机制之一。它负责调度和执行协程并处理协程之间的切换。事件循环将不断轮询可执行任务。一旦任务准备就绪(例如 I/O 操作完成或计时器到期),事件循环会将其放入执行队列并继续执行下一个任务。
  3. 异步任务:在asyncio中,我们通过创建异步任务来执行协程。异步任务由 asyncio.create_task() 函数创建,该函数将协程封装成一个可等待的对象,并将其提交给事件循环进行处理。
  4. 异步I/O操作:asyncio提供了一组异步I/O操作(如网络请求、文件读写等),可以通过等待关键字。通过使用异步I/O操作,可以避免等待I/O完成期间的阻塞,提高程序性能和并发性。
  5. 回调:asyncio还支持使用回调函数来处理异步操作的结果。 asyncio.ensure_future() 函数可用于将回调函数封装成可等待对象,并将其提交给事件循环进行处理。
  6. 并发执行:asyncio可以并发执行多个协程任务。事件循环会根据任务的就绪情况自动调度协程的执行,从而实现高效的并发编程。

综上所述,asyncio的工作原理是基于协程和事件循环的机制。 asyncio 通过使用协程进行异步操作,并让事件循环负责协程的调度和执行,实现了高效的异步编程模型。

协程和异步编程

协程是 asyncio 中的一个重要概念。它们是轻量级执行单元,可以在任务之间快速切换,而无需线程切换的开销。可以使用 async 关键字定义协程,await 关键字用于暂停协程的执行,并在某个操作完成后恢复。

这是一个简单的示例代码,演示如何使用协程进行异步编程:

import asyncio

async def hello():
    print("Hello")
    await asyncio.sleep(1)  # Simulate a time-consuming operation
    print("World")

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(hello())
登录后复制
登录后复制

在此示例中,函数 hello() 是使用 async 关键字定义的协程。在协程内部,我们可以使用await来暂停其执行。这里使用asyncio.sleep(1)来模拟一个耗时的操作。 run_until_complete() 方法将协程添加到事件循环中并运行它。

异步 I/O 操作

asyncio主要用于处理I/O密集型任务,如网络请求、文件读写等。它提供了一系列异步I/O操作的API,可以与await关键字结合使用,轻松实现异步编程。

这是一个简单的示例代码,展示了如何使用 asyncio 进行异步网络请求:

import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        html = await fetch(session, 'https://www.example.com')
        print(html)

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(main())
登录后复制
登录后复制

在此示例中,我们使用 aiohttp 库来处理网络请求。函数 fetch() 是一个协程。它通过session.get()方法发起异步GET请求,并使用await关键字等待响应返回。函数 main() 是另一个协程。它在内部创建一个 ClientSession 对象以供复用,然后调用 fetch() 方法获取网页内容并打印。

注意:这里我们使用aiohttp而不是requests库,因为requests库不兼容asyncio,而aiohttp库则兼容。想要用好asyncio,尤其是发挥其强大的功能,很多时候需要相应的Python库。

多个任务并发执行

asyncio还提供了一些并发执行多个任务的机制,例如asyncio.gather()和asyncio.wait()。下面是一个示例代码,展示了如何使用这些机制来并发执行多个协程任务:

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())

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(main())
登录后复制
登录后复制

在这个例子中,我们定义了两个协程任务task1()和task2(),它们都执行一些耗时的操作。协程 main() 通过 asyncio.gather() 同时启动这两个任务并等待它们完成。并发执行可以提高程序执行效率。

如何选择?

实际项目中,我们应该选择多线程还是asyncio?有大佬总结的很形象:

import asyncio

async def hello():
    print("Hello")
    await asyncio.sleep(1)  # Simulate a time-consuming operation
    print("World")

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(hello())
登录后复制
登录后复制
  • 如果是 I/O 绑定,并且 I/O 操作很慢,需要很多任务/线程的配合,那么使用 asyncio 更合适。
  • 如果是 I/O 绑定,但 I/O 操作很快并且只需要有限数量的任务/线程,那么多线程就可以了。
  • 如果是CPU密集型,那么就需要多处理来提高程序运行效率。

实践

输入一个列表。对于列表中的每个元素,我们要计算从 0 到该元素的所有整数的平方和。

同步实施

import asyncio
import aiohttp

async def fetch(session, url):
    async with session.get(url) as response:
        return await response.text()

async def main():
    async with aiohttp.ClientSession() as session:
        html = await fetch(session, 'https://www.example.com')
        print(html)

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(main())
登录后复制
登录后复制

执行时间为计算耗时16.00943413000002秒

使用并发.futures 进行异步实现

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())

# Create an event loop
loop = asyncio.get_event_loop()

# Add the coroutine to the event loop and execute
loop.run_until_complete(main())
登录后复制
登录后复制

执行时间为计算耗时 7.314132894999999 秒

在这段改进后的代码中,我们使用concurrent.futures.ProcessPoolExecutor创建进程池,然后使用executor.map()方法提交任务并获取结果。注意,使用 executor.map() 后,如果需要获取结果,可以将结果迭代到列表中,或者使用其他方法处理结果。

多处理实现

if io_bound:
    if io_slow:
        print('Use Asyncio')
    else:
        print('Use multi-threading')
elif cpu_bound:
    print('Use multi-processing')
登录后复制

执行时间为计算耗时5.024221667秒

concurrent.futures.ProcessPoolExecutor和multiprocessing都是Python中用于实现多进程并发的库。有一些差异:

  1. 基于接口的封装:concurrent.futures.ProcessPoolExecutor是concurrent.futures模块提供的高级接口。它封装了底层的多进程函数,使编写多进程代码变得更加容易。而multiprocessing是Python的标准库之一,提供完整的多进程支持,并允许直接对进程进行操作。
  2. API 用法:concurrent.futures.ProcessPoolExecutor 的用法与线程池类似。它将可调用的对象(例如函数)提交到进程池执行,并返回一个Future对象,可以用来获取执行结果。多处理提供了更多低级进程管理和通信接口。可以显式地创建、启动和控制进程,并且可以使用队列或管道来完成多个进程之间的通信。
  3. 可扩展性和灵活性:由于多处理提供了更多底层接口,因此与concurrent.futures.ProcessPoolExecutor相比更加灵活。通过直接操作进程,可以对每个进程实现更细粒度的控制,如设置进程优先级、进程间共享数据等。 concurrent.futures.ProcessPoolExecutor 更适合简单的任务并行化,隐藏了很多底层细节,更容易编写多进程代码。
  4. 跨平台支持:concurrent.futures.ProcessPoolExecutor 和 multiprocessing 都提供跨平台多进程支持,可以在各种操作系统上使用。

综上所述,concurrent.futures.ProcessPoolExecutor是一个高层接口,封装了底层多进程功能,适合简单的多进程任务并行化。 multiprocessing是一个更底层的库,提供了更多的控制和灵活性,适合需要对流程进行细粒度控制的场景。您需要根据具体需求选择合适的库。如果只是简单的任务并行化,可以使用concurrent.futures.ProcessPoolExecutor来简化代码;如果需要更多底层控制和通信,您可以使用多处理库。

概括

与多线程不同,asyncio 是单线程的,但其内部事件循环的机制允许它同时运行多个不同的任务,并且比多线程具有更强的自主控制能力。

asyncio中的任务在运行过程中不会被中断,因此不会出现race condition的情况。

尤其是在I/O操作繁重的场景下,asyncio比多线程有更高的运行效率。因为asyncio中任务切换的成本远小于线程切换的成本,而且asyncio可以启动的任务数量远大于多线程中的线程数量。

但是需要注意的是,很多情况下,使用asyncio需要特定的第三方库的支持,比如上例中的aiohttp。而如果I/O操作又快又不重的话,使用多线程也能有效解决问题。

  • asyncio 是一个用于实现异步编程的 Python 库。
  • 协程是asyncio的核心概念,通过async和await关键字实现异步操作。
  • asyncio 为异步 I/O 操作提供了强大的 API,可以轻松处理 I/O 密集型任务。
  • 通过asyncio.gather()等机制,可以并发执行多个协程任务。

Leapcell:FastAPI、Flask 和其他 Python 应用程序的理想平台

最后介绍一下部署Flask/FastAPI的理想平台:Leapcell。

Leapcell是专为现代分布式应用程序设计的云计算平台。其按需付费的定价模式确保没有闲置成本,这意味着用户只需为他们实际使用的资源付费。

High-Performance Python: Asyncio

  1. 多语言支持
    • 支持 JavaScript、Python、Go 或 Rust 开发。
  2. 免费部署无限项目
    • 仅根据使用情况收费。没有要求时不收费。
  3. 无与伦比的成本效益
    • 即用即付,无闲置费用。
    • 例如,25 美元可以支持 694 万个请求,平均响应时间为 60 毫秒。
  4. 简化的开发者体验
    • 直观的用户界面,易于设置。
    • 完全自动化的 CI/CD 管道和 GitOps 集成。
    • 实时指标和日志,提供可操作的见解。
  5. 轻松的可扩展性和高性能
    • 自动伸缩,轻松应对高并发。
    • 零运营开销,让开发者专注于开发。

在文档中了解更多信息!
Leapcell Twitter:https://x.com/LeapcellHQ

以上是高性能 Python:Asyncio的详细内容。更多信息请关注PHP中文网其他相关文章!

来源:dev.to
本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
作者最新文章
热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板