Python协程、异步IO与asyncio

2023-11-17 21:51:46 浏览数 (3)

Python,作为一门流行的编程语言,不仅具备清晰简洁的语法和强大的生态系统,还在异步编程领域拥有丰富而灵活的工具,其中包括协程、异步IO以及asyncio库。本文将深入探讨这些概念的本质,解释它们的原理,并通过实际示例演示如何应用它们,帮助我们更好地理解和利用Python的异步编程能力。

概念介绍

Python协程

  • 协程是一种轻量级的线程,用于非阻塞异步编程。
  • 通过async和await关键字定义,使得函数可以在执行中暂停和恢复。
  • 协程可以用于处理高并发的I/O密集型任务,而无需使用多线程或多进程的复杂性。
  • 通常与异步IO一起使用,以实现高效的非阻塞IO操作。

异步IO

  • 异步IO是一种编程模型,用于处理非阻塞IO操作,以提高程序的并发性能。
  • 它允许一个程序同时处理多个IO操作,而不需要等待每个IO操作完成。
  • 在Python中,异步IO通常与协程一起使用,以实现高效的非阻塞IO编程。

asyncio

  • asyncio是Python标准库中的异步IO库,用于编写基于协程的异步程序。
  • 它提供了事件循环(event loop),用于调度和执行协程任务。
  • asyncio包含了许多工具和函数,用于处理异步IO操作,例如网络通信、文件IO等。
  • asyncio是Python中异步编程的核心库,可以用于构建高性能的异步应用程序,如Web服务器、聊天应用程序等。

协程(Coroutines)

    协程是一种轻量级的线程,它允许函数在执行过程中暂停并恢复。与常规函数不同,协程具有多个入口点,可以在函数内部的任何位置暂停和继续执行。Python的协程通过async和await关键字来定义和管理。

基本概念

在Python中,协程的基本概念如下:

  • async def:通过在函数定义前添加async关键字,可以将普通函数变成协程函数。协程函数可以在执行过程中暂停。
  • await:await关键字用于在协程中等待另一个协程或异步操作完成。当执行到await语句时,协程将暂停,直到等待的操作完成。

示例

下面是一个简单的协程示例,演示了如何使用协程来实现异步任务:

代码语言:javascript复制
import asyncio
async def hello(arg):
    print("Hello : ",arg)
    if arg == "SRE 1":
        await asyncio.sleep(2)
    else:
        await asyncio.sleep(1)
    print("World : ",arg)
    print("bingo!!")
async def main():
    await asyncio.gather(hello("SRE 1"), hello("SRE 2"), hello("SRE 3"))
asyncio.run(main())

运行可以看到日志

代码语言:javascript复制
Hello :  SRE 1
Hello :  SRE 2
Hello :  SRE 3
World :  SRE 2
bingo!!
World :  SRE 3
bingo!!
World :  SRE 1
bingo!!

    在这个示例中,hello函数是一个协程,通过await asyncio.sleep(1)来模拟一个耗时的操作。main函数使用await asyncio.gather()来同时运行多个协程。

异步IO(Asynchronous IO)

异步IO是一种编程模型,用于处理非阻塞的IO操作。它使程序能够在执行IO操作时继续执行其他任务,而不必等待IO操作完成。

基本概念

异步IO的核心概念包括:

  • 非阻塞IO:异步IO允许执行非阻塞的IO操作,这意味着程序在等待IO完成时不会被阻塞。
  • 事件循环:异步IO通常使用事件循环来管理协程和异步任务的调度。事件循环负责将协程放入等待IO的队列,并在IO完成时恢复它们的执行。

示例

以下示例演示了如何使用异步IO进行文件读取操作:

代码语言:javascript复制
import asyncio
# 定义一个异步函数,模拟一个非阻塞的IO操作
async def non_blocking_io_operation(arg):
    print("开始非阻塞IO操作")
    if arg == "task1":
        await asyncio.sleep(2)  # 模拟IO操作,这里使用await来模拟异步操作
    else:
        await asyncio.sleep(5)
    print(f"完成调用:{arg} 的非阻塞IO操作")
# 创建一个事件循环
async def main():
    # 将非阻塞IO操作放入事件循环中
    task1 = asyncio.create_task(non_blocking_io_operation("task1"))
    task2 = asyncio.create_task(non_blocking_io_operation("task2"))
    # 等待所有任务完成
    await task1
    print("完成task1 IO执行!")
    await task2
    print("完成task2 IO执行!")
# 启动事件循环
if __name__ == "__main__":
    asyncio.run(main())

执行输出日志

代码语言:javascript复制
开始非阻塞IO操作
开始非阻塞IO操作
完成调用:task1 的非阻塞IO操作
完成task1 IO执行!
完成调用:task2 的非阻塞IO操作
完成task2 IO执行!复制

asyncio库

asyncio 是异步 I/O 的缩写。它是一个 Python 库,允许我们使用异步编程模型运行代码。这让我们可以同时处理多个 I/O 操作,同时仍然允许我们的应用程序保持响应能力。

Python 3.4 引入了 asyncio 库,Python 3.5 生成了 async 和await 关键字以方便地使用它。这些新增功能允许所谓的异步编程。

基本概念

asyncio库的基本概念包括:

  • 事件循环(Event Loop):事件循环是异步程序的核心,负责调度协程和处理异步任务的完成。
  • asyncio.run():这个函数用于运行主协程,它在Python 3.7及更高版本中可用。
  • asyncio.create_task():用于创建并调度协程任务。

简单示例

以下示例展示了如何使用asyncio库来并发执行多个协程任务:

代码语言:javascript复制
import asyncio
async def task1():
    print("into task1 ....")
    await asyncio.sleep(2)
    print("Task 1 completed")
async def task2():
    print("into task2 ...")
    await asyncio.sleep(1)
    print("Task 2 completed")
async def main():
    task1_handle = asyncio.create_task(task1())
    task2_handle = asyncio.create_task(task2())
    await task1_handle
    await task2_handle
asyncio.run(main())
代码语言:javascript复制
在这个示例中,我们使用asyncio.create_task()创建了两个协程任务,并且可以并发执行它们。await关键字用于等待任务完成。

异步事件循环

    事件循环是 asyncio 应用程序的核心,负责处理所有正在运行的任务。事件循环支持多任务处理。当一个函数被挂起时,控制权返回到循环,然后循环找到另一个函数来启动或恢复。

使用事件循环的语法

代码语言:javascript复制
loop = asyncio.get_event_loop()
loop.run_until_complete(my_coroutine())
loop.close()

示例

代码语言:javascript复制
import asyncio
import time
async def cor1():
    print("cor 1 started")
    for i in range(5):
        await asyncio.sleep(1.5)
        print("cor1", i)
async def cor2():
    print("cor 2 started")
    for i in range(8):
        await asyncio.sleep(1)
        print("cor2", i)
loop = asyncio.get_event_loop()
cors = asyncio.wait([cor1(), cor2()])
loop.run_until_complete(cors)复制

运行可以得到输出

代码语言:javascript复制
cor 2 started
cor 1 started
cor2 0
cor1 0
cor2 1
cor1 1
cor2 2
cor2 3
cor1 2
cor2 4
cor1 3
cor2 5
cor2 6
cor1 4
cor2 7

任务

任务用于同时调度协程。我们使用函数 create_task() 创建一个任务。使用此函数将协程包装到任务中。

示例

代码语言:javascript复制
import asyncio
import time
async def main():
  # 使用 asyncio.create_task() 方法创建任务
    task1 = asyncio.create_task(foo('task 1 run'))
    task2 = asyncio.create_task(foo('task 2 run'))
    print(f"started at {time.strftime('%X')}")
    # 等待所有任务执行完成
    await task1
    await task2
    print(f"finished at {time.strftime('%X')}")
async def foo(text):
    print(text)
    await asyncio.sleep(1)
asyncio.run(main())

得到输出

代码语言:javascript复制
started at 17:35:47
task 1 run
task 2 run
finished at 17:35:48

同时运行任务

为了执行并发执行,我们使用函数 Gather 如下。可等待的 asyncio.gather(*aws, return_exceptions=False)

该函数采用任意数量的可等待项(协程、任务等)作为参数。

示例

代码语言:javascript复制
import asyncio
# 定义一个异步函数,用于打印文本并在每次打印后等待一段时间
async def printing(task, text):
    while True:
        print(task, text)
        try:
            await asyncio.sleep(0.50)  # 等待0.50秒,模拟异步操作
        except asyncio.CancelledError:
            break  # 如果收到取消请求,跳出循环结束任务
async def main():
    try:
        # 使用 asyncio.wait_for 设置一个超时时间为 3 秒,同时并发运行三个打印任务
        await asyncio.wait_for(
            asyncio.gather(
                printing("A", "task 1"),
                printing("B", "task 2"),
                printing("C", "task 3")
            ), 3  # 超时时间设置为 3 秒
        )
    except asyncio.TimeoutError:
        print("Time over")  # 如果超时,打印 "Time over" 提示
asyncio.run(main())

得到输出

代码语言:javascript复制
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
A task 1
B task 2
C task 3
Time over
_GatheringFuture exception was never retrieved
future: <_GatheringFuture finished exception=CancelledError()>
concurrent.futures._base.CancelledError

异步编程提高了应用程序性能和响应能力。Python 提供了支持异步编程的不同库。Asyncio允许我们使用 async/await 语法编写并发代码。Asyncio 由协程、任务、流、子进程、异常等高级 API 和事件循环、Futures、传输和协议等低级 API 组成。

可以查看原文:

https://mp.weixin.qq.com/s?__biz=MzA5NTgwNzY1NA==&mid=2247484074&idx=1&sn=82db976658d234dc16350a85c2709f23&chksm=90b8f363a7cf7a75c1315d099d573fc15075dfd5ef8637797cf57a7e3ae6d9250cb97e67a2ce&token=211557010&lang=zh_CN#rd

0 人点赞