并发编程是一种处理多个任务同时执行的编程方法。在Python中,asyncio是实现异步编程的强大工具。基于协程的概念,asyncio 可以高效地处理 I/O 密集型任务。本文将介绍asyncio的基本原理和使用方法
我们知道,在处理I/O操作时,使用多线程相比普通的单线程可以大大提高效率。那么,为什么我们还需要 asyncio?
多线程有很多优点,应用广泛,但也有一定的局限性:
正是为了解决这些问题,asyncio 应运而生。
我们先区分一下Sync(同步)和Async(异步)的概念。
综上所述,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() 方法将协程添加到事件循环中并运行它。
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())
输入一个列表。对于列表中的每个元素,我们要计算从 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秒
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中用于实现多进程并发的库。有一些差异:
综上所述,concurrent.futures.ProcessPoolExecutor是一个高层接口,封装了底层多进程功能,适合简单的多进程任务并行化。 multiprocessing是一个更底层的库,提供了更多的控制和灵活性,适合需要对流程进行细粒度控制的场景。您需要根据具体需求选择合适的库。如果只是简单的任务并行化,可以使用concurrent.futures.ProcessPoolExecutor来简化代码;如果需要更多底层控制和通信,您可以使用多处理库。
与多线程不同,asyncio 是单线程的,但其内部事件循环的机制允许它同时运行多个不同的任务,并且比多线程具有更强的自主控制能力。
asyncio中的任务在运行过程中不会被中断,因此不会出现race condition的情况。
尤其是在I/O操作繁重的场景下,asyncio比多线程有更高的运行效率。因为asyncio中任务切换的成本远小于线程切换的成本,而且asyncio可以启动的任务数量远大于多线程中的线程数量。
但是需要注意的是,很多情况下,使用asyncio需要特定的第三方库的支持,比如上例中的aiohttp。而如果I/O操作又快又不重的话,使用多线程也能有效解决问题。
最后介绍一下部署Flask/FastAPI的理想平台:Leapcell。
Leapcell是专为现代分布式应用程序设计的云计算平台。其按需付费的定价模式确保没有闲置成本,这意味着用户只需为他们实际使用的资源付费。
在文档中了解更多信息!
Leapcell Twitter:https://x.com/LeapcellHQ
以上是高性能 Python:Asyncio的详细内容。更多信息请关注PHP中文网其他相关文章!